Django unique_together 不适用于ForeignKey=None

发布于 2024-09-14 09:38:51 字数 476 浏览 4 评论 0原文

我看到一些人在我之前遇到了这个问题,但是在旧版本的 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')

每当我尝试在管理中保存一个父级设置为“无”的类别时,当存在另一个具有相同名称且父级设置为“无”的类别时,它仍然有效。

关于如何优雅地解决这个问题的想法?

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

Whenever i try to save in the admin a category with a parent set to None, it still works when there's another category with the SAME name and parent set to None.

Ideas on how to solve this gracefully?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

浮华 2024-09-21 09:38:51

唯一的共同约束是在数据库级别强制执行的,并且您的数据库引擎似乎没有对空值应用该约束。

在 Django 1.2 中,您可以定义 clean 方法 为您的模型提供自定义验证。就您而言,您需要在父级为“无”时检查具有相同名称的其他类别的东西。

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

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().

魂ガ小子 2024-09-21 09:38:51

我也遇到了这个问题,并通过使用 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)

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)
非要怀念 2024-09-21 09:38:51

不幸的是,对于我们这些使用 PostgreSQL 作为后端数据库引擎的人来说,这个问题永远不会有解决方案:

“目前,只有 B 树索引可以声明为唯一。

当索引声明为唯一时,不允许具有相等索引值的多个表行被视为相等。多列唯一索引只会拒绝多行中所有索引列都相等的情况

。是为表定义的。索引涵盖构成主键或唯一约束(多列索引,如果适用)的列,并且是强制执行约束的机制。”

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

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文