Django unique_together не работает с ForeignKey = Нет - PullRequest
8 голосов
/ 15 августа 2010

Я видел, что у некоторых людей эта проблема была до меня, но на старых версиях Django, и я работаю на 1.2.1.

У меня есть модель, которая выглядит следующим образом:

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

Всякий раз, когда я пытаюсь сохранить в администраторе категорию с родителем, для которого задано значение Нет, она все равно работает, когда есть другая категория с тем же именем и родителем, для которого установлено значение Нет.

Идеи о том, как решить это изящно?

Ответы [ 3 ]

10 голосов
/ 15 августа 2010

На уровне базы данных применяется уникальное ограничение вместе, и кажется, что ваш движок базы данных не применяет ограничение для нулевых значений.

В Django 1.2 вы можете определить чистый метод для вашей модели, чтобы обеспечить пользовательскую проверку.В вашем случае вам понадобится что-то, что проверяет другие категории с тем же именем, когда родитель имеет значение 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 is None and Category.objects.filter(name=self.name, parent=None).exists():
            raise ValidationError("Another Category with name=%s and no parent already exists" % self.name)

Если вы редактируете категории через администратора Django, метод clean будет вызываться автоматически.По вашему собственному мнению, вы должны позвонить category.fullclean().

5 голосов
/ 26 января 2011

У меня тоже была эта проблема, и я решил ее, создав супермодель с методом clean (как предложил Alasdair) и использовал его в качестве базового класса для всех моих моделей:

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)
1 голос
/ 09 марта 2019

К сожалению, для тех из нас, кто использует PostgreSQL в качестве нашего механизма базы данных, никогда не будет исправлено это:

"В настоящее время только индексы B-дерева могут быть объявлены уникальными.

Когда индекс объявляется уникальным, несколько строк таблицы с одинаковыми индексированными значениями не допускаются. Нулевые значения не считаются равными. Уникальный индекс из нескольких столбцов будет отклонять только те случаи, когда все индексированные столбцы равны в нескольких строках.

PostgreSQL автоматически создает уникальный индекс, когда для таблицы определено уникальное ограничение или первичный ключ. Индекс охватывает столбцы, которые составляют первичный ключ или уникальное ограничение (многоколоночный индекс, если необходимо), и является механизмом, который применяет ограничение. "

Источник: https://www.postgresql.org/docs/9.0/indexes-unique.html

...