有没有更优雅的方法在Django模型中添加一个值敏感的唯一一起约束?

时间:2013-05-10 03:06:31

标签: python django django-models django-database

问题在于:

我有一个这样的模型:

class UserBook(models.Model):
    user = models.ForeignKey(User)
    book = models.ForeignKey(Book)
    is_active = models.BooleanField(default=False)

    class Meta:
        unique_together = ("user", "book")

显然,此模型已经对字段用户图书具有唯一的约束约束。可能在数据库中会有这样的条目:

    ------------------------------
    |user_id  book_id  is_active |
    |      1        1          0 |
    |      1        2          0 |
    |      1        3          1 |
    ------------------------------

我还有一个要添加的约束,即每个用户最多只能有一个条目 is_active 字段的值为1(True)。

目前我通过将模型更改为:

来解决此问题
class UserBook(models.Model):
    user = models.ForeignKey(User)
    book = models.ForeignKey(Book)
    is_active = models.BooleanField(default=False)
    key = models.charFeild(max_length=255, unique=True)

    class Meta:
        unique_together = ("user", "book")

    def save(self, *args, **kwargs):
        if self.is_active:
            self.key = "%s_%s" %(self.user_id, self.is_active)
        else:
            self.key = "%s_%s_%s" %(self.user_id, self.is_active, self.book_id)

添加字段,并自定义此模型的保存方法。

max_length 在此方法中不能大于255(在我的情况下不需要担心,但有时字段可能会很长)。

所以,我想知道是否有更优雅的方法来解决这类问题。

谢谢!

4 个答案:

答案 0 :(得分:4)

根据Nour的回答,您可以执行以下操作:

class Meta:
    constraints = [
        models.UniqueConstraint(
            fields=['user'],
            condition=Q(is_active=True),
            name='unique active user book per user'
        ),
    ]

答案 1 :(得分:2)

重新定义is_active如下:

# Equals user ID if active; otherwise null.
is_active = models.IntegerField(null = True, unique = True)

用户ID在列中是唯一的(满足您​​所需的约束),并且列中的许多空值不会违反约束,如here所述。

答案 2 :(得分:1)

Django 2.2(当前以beta1版本发布)中,您将可以使用UniqueConstraint,除了fields列表之外,还可以传递condition

  

Q 对象,用于指定您希望约束强制执行的条件。

     

例如, UniqueConstraint(fields = ['user'],condition = Q(status ='DRAFT')确保每个用户只有一个草稿。

答案 3 :(得分:0)

您只需要在唯一约束中包含is_active

class UserBook(models.Model):
    user = models.ForeignKey(User)
    book = models.ForeignKey(Book)
    is_active = models.BooleanField(default=False)

    class Meta:
        unique_together = ("user", "book", "is_active")

也就是说,我建议用户重新激活一本书,而不是使用非活动版本和活动版本。

旁注,请参阅many-to-many through了解一些额外的好处(您可以使用User.books作为UserBook表,将through添加为多对多。