2010-08-15 24 views
7

Vi que algunas personas tenían este problema antes que yo, pero en las versiones anteriores de Django, y me estoy ejecutando en 1.2.1.Django unique_together no funciona con ForeignKey = Ninguna

que tienen un modelo que se parece a:

class Category(models.Model): 
objects = CategoryManager() 

name = models.CharField(max_length=30, blank=False, null=False) 
parent = models.ForeignKey('self', null=True, blank=True, help_text=_('The direct parent category.')) 

class Meta: 
    unique_together = ('name', 'parent') 

cada vez que intento de salvar en la administración de una categoría con un padre en Ninguno, todavía funciona cuando hay otra categoría con el nombre MISMO y conjunto de padres a ninguno

Ideas sobre cómo resolver esto con gracia?

Respuesta

9

La restricción unique together se aplica en el nivel de la base de datos, y parece que su motor de base de datos no aplica la restricción para los valores nulos.

En Django 1.2, puede definir un clean method para su modelo para proporcionar una validación personalizada. En su caso, necesita algo que busque otras categorías con el mismo nombre siempre que el padre sea Ninguno.

class Category(models.Model): 
    ... 
    def clean(self): 
     """ 
     Checks that we do not create multiple categories with 
     no parent and the same name. 
     """ 
     from django.core.exceptions import ValidationError 
     if self.parent and Category.objects.filter(name=self.name).exists(): 
      raise ValidationError("Another Category with name=%s and no parent already exists % self.name) 

Si está editando categorías a través de la administración de Django, el método de limpieza se llamará automáticamente. En sus propios puntos de vista, debe llamar al category.fullclean().

+0

El enfoque general se ve bien aquí, pero No estoy siguiendo la lógica de 'if self.parent y Category.objects.filter (name = self.name) .exists():' Eso me parece que está comprobando que el padre existe y otra categoría con el mismo nombre existe ¿Cómo es esto lo que queremos? ¿No debería ser más bien algo así como (no probado) 'si self.parent == None y FolderUpload.objects.filter (name = self.name, parent = None) .exists():'? –

+0

Creo que tienes razón. Yo usaría parent_id__is null = True en lugar de parent = None. Probablemente necesite una exclusión() para ignorar el objeto actual también. – Alasdair

+0

Estaré ausente durante una semana, por lo que no podré corregir la respuesta. Siéntase libre de editarlo si lo desea/puede. – Alasdair

5

tuve ese problema y lo resolvió mediante la creación de una supermodelo con clean método (como Alasdair sugirió) y utilizarlo como clase base para todos mis modelos:

class Base_model(models.Model): 
    class Meta: 
    abstract=True 

    def clean(self): 
    """ 
    Check for instances with null values in unique_together fields. 
    """ 
    from django.core.exceptions import ValidationError 

    super(Base_model, self).clean() 

    for field_tuple in self._meta.unique_together[:]: 
     unique_filter = {} 
     unique_fields = [] 
     null_found = False 
     for field_name in field_tuple: 
      field_value = getattr(self, field_name) 
      if getattr(self, field_name) is None: 
       unique_filter['%s__isnull'%field_name] = True 
       null_found = True 
      else: 
       unique_filter['%s'%field_name] = field_value 
       unique_fields.append(field_name) 
     if null_found: 
      unique_queryset = self.__class__.objects.filter(**unique_filter) 
      if self.pk: 
       unique_queryset = unique_queryset.exclude(pk=self.pk) 
      if unique_queryset.exists(): 
       msg = self.unique_error_message(self.__class__, tuple(unique_fields)) 
       raise ValidationError(msg) 
Cuestiones relacionadas