2010-08-15 7 views
7

Ich sah einige ppl hatte dieses Problem vor mir, aber auf älteren Versionen von Django, und ich bin auf 1.2.1 ausgeführt.Django unique_together funktioniert nicht mit ForeignKey = Keine

ich ein Modell, das wie folgt aussieht:

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

Jedes Mal, wenn ich versuche, mit einem Elternteil eine Kategorie in der Admin speichern auf Kein setzen, es funktioniert immer noch, wenn es eine andere Kategorie mit dem gleichen Namen und Elternsatz Zu keiner.

Ideen, wie man das elegant löst?

Antwort

9

Die Einschränkung "zusammen zusammen" wird auf Datenbankebene erzwungen, und es scheint, dass Ihr Datenbankmodul die Einschränkung nicht für Nullwerte anwendet.

In Django 1.2 können Sie eine clean method für Ihr Modell definieren, um benutzerdefinierte Validierung bereitzustellen. In Ihrem Fall benötigen Sie etwas, das nach anderen Kategorien mit demselben Namen sucht, wenn das übergeordnete Element Keine ist.

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) 

Wenn Sie Kategorien über den Django-Administrator bearbeiten, wird die clean-Methode automatisch aufgerufen. In Ihren eigenen Ansichten müssen Sie category.fullclean() aufrufen.

+0

Der allgemeine Ansatz sieht hier gut, aber ich folge nicht der Logik von 'if selb.parent und Category.objects.filter (name = self.name) .exists():' Das sieht für mich so aus, als ob es prüft, ob der Elternteil existiert und eine andere Kategorie mit dem gleichen Namen existiert. Wie wollen wir das? Sollte dies nicht etwas wie (ungetestet) sein? If self.parent == None und FolderUpload.objects.filter (name = self.name, parent = None) .exists(): '? –

+0

Ich denke, du hast Recht. Ich würde parent_id__is null = True anstelle von parent = None verwenden. Es benötigt wahrscheinlich eine exclude(), um das aktuelle Objekt ebenfalls zu ignorieren. – Alasdair

+0

Ich werde für eine Woche weg sein, also wird nicht in der Lage sein, die Antwort zu korrigieren. Fühlen Sie sich frei, es zu bearbeiten, wenn Sie wollen/können. – Alasdair

5

hatte ich dieses Problem zu und löste es durch ein Topmodel mit clean Verfahren zu schaffen (wie Alasdair vorgeschlagen) und verwenden Sie es als Basisklasse für alle meine Modelle:

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) 
Verwandte Themen