Django unique_together doesn't work with ForeignKey=None

前端 未结 3 1343
抹茶落季
抹茶落季 2021-01-05 03:40

I saw some ppl had this problem before me, but on older versions of Django, and I\'m running on 1.2.1.

I have a model that looks like:

class Category         


        
相关标签:
3条回答
  • 2021-01-05 03:52

    I had that problem too and solved it by creating a supermodel with clean method (like Alasdair suggested) and use it as base class for all my models:

    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)
    
    0 讨论(0)
  • 2021-01-05 04:14

    The unique together constraint is enforced at the database level, and it appears that your database engine does not apply the constraint for null values.

    In Django 1.2, you can define a clean method for your model to provide custom validation. In your case, you need something that checks for other categories with the same name whenever the parent is 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)
    

    If you are editing categories through the Django admin, the clean method will be called automatically. In your own views, you must call category.fullclean().

    0 讨论(0)
  • 2021-01-05 04:18

    Unfortunately, for those of us using PostgreSQL as our backend database engine, there will never have a fix for this issue:

    "Currently, only B-tree indexes can be declared unique.

    When an index is declared unique, multiple table rows with equal indexed values are not allowed. Null values are not considered equal. A multicolumn unique index will only reject cases where all indexed columns are equal in multiple rows.

    PostgreSQL automatically creates a unique index when a unique constraint or primary key is defined for a table. The index covers the columns that make up the primary key or unique constraint (a multicolumn index, if appropriate), and is the mechanism that enforces the constraint."

    Source: https://www.postgresql.org/docs/9.0/indexes-unique.html

    0 讨论(0)
提交回复
热议问题