2010-08-15 7 views
7

J'ai vu que certains ppl avaient ce problème avant moi, mais sur les anciennes versions de Django, et je cours sur 1.2.1.Django unique_together ne fonctionne pas avec ForeignKey = Aucun

J'ai un modèle qui ressemble à:

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') 

Chaque fois que je tente de sauver dans l'admin une catégorie avec un parent à None, il fonctionne encore quand il y a une autre catégorie avec le même nom et ensemble parent à Aucun.

Comment résoudre ce problème avec élégance?

Répondre

9

La contrainte unique ensemble est appliquée au niveau de la base de données et il semble que votre moteur de base de données n'applique pas la contrainte pour les valeurs nulles.

Dans Django 1.2, vous pouvez définir un clean method pour votre modèle afin de fournir une validation personnalisée. Dans votre cas, vous avez besoin de quelque chose qui vérifie les autres catégories avec le même nom lorsque le parent est None.

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 vous éditez des catégories via l'admin Django, la méthode clean sera appelée automatiquement. Dans vos propres vues, vous devez appeler category.fullclean().

+0

L'approche générale semble bon, mais je ne suis pas la logique de 'si self.parent et Category.objects.filter (name = self.name) .exists():' Cela me semble comme il vérifie que le parent existe et une autre catégorie avec le même nom existe. Comment est-ce ce que nous voulons? Cela ne devrait-il pas plutôt être quelque chose comme (non testé) 'if self.parent == None et FolderUpload.objects.filter (nom = self.name, parent = None) .exists():'? –

+0

Je pense que vous avez raison. J'utiliserais parent_id__is null = True au lieu de parent = None. Il a probablement besoin d'un exclude() pour ignorer l'objet courant. – Alasdair

+0

Je serai absent pendant une semaine, donc je ne serai pas en mesure de corriger la réponse. N'hésitez pas à l'éditer si vous le souhaitez/pouvez. – Alasdair

5

J'ai eu aussi ce problème et résolu en créant un top model avec la méthode clean (comme Alasdair suggéré) et l'utiliser comme classe de base pour tous mes modèles:

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) 
Questions connexes