简体   繁体   English

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

[英]Is there any more elegant way to add a value sensitive unique together constraint in Django Model?

Here is the problem:这是问题所在:

I have a model like this:我有一个这样的模型:

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

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

Obviously, this model already has a unique together constraint for field user and book .显然,该模型已经对字段userbook具有唯一的共同约束。 And probably there will be some entries like this in the database:并且数据库中可能会有一些这样的条目:

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

And I have one more constraint to add, which is each user can have at most one entry that the value of is_active field is 1(True).我还有一个约束要添加,即每个用户最多可以有一个is_active字段的值为 1(True) 的条目。

Currently I solve this problem by changing the model into this:目前我通过将模型更改为这个来解决这个问题:

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)

Add a field key , and customize the save method of this model.添加字段key ,自定义该模型的保存方式。

But the max_length cannot be greater than 255 in this approach(which is no need to worry in my case, but sometimes the key field may be very long).但是在这种方法中max_length不能大于 255(在我的情况下不需要担心,但有时关键字段可能很长)。

So, I would like to know if there is any more elegant approach to solve this kind of problem.所以,我想知道是否有更优雅的方法来解决这类问题。

Thanks!谢谢!

In Django 2.2 (currently released as beta1) you will be able to use UniqueConstraint which in addition to the list of fields can be passed a conditionDjango 2.2 (目前作为 beta1 发布)中,您将能够使用UniqueConstraint ,除了fields列表之外,还可以传递一个condition

A Q object that specifies the condition you want the constraint to enforce.一个Q对象,指定您希望约束强制执行的条件。

For example, UniqueConstraint(fields=['user'], condition=Q(status='DRAFT') ensures that each user only has one draft.例如, UniqueConstraint(fields=['user'], condition=Q(status='DRAFT')确保每个用户只有一个草稿。

Based on Nour's answer, you can do this:根据 Nour 的回答,您可以这样做:

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

Redefine the is_active to be as follows:重新定义is_active如下:

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

The user IDs will be unique in the column (satisfying your desired constraint) and the many null values in the column won't violate the constraint, as discussed here .该用户ID将在列中唯一(满足您所需的约束)和列的许多空值不会违反约束,为讨论在这里

You just need to include is_active in the unique constraint. 您只需要在唯一约束中包含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")

That said, I would recommend having the user re-activate a book, rather than having an inactive and an active version. 也就是说,我建议用户重新激活一本书,而不是使用非活动版本和活动版本。

Side note, see many-to-many through for some extra goodness (you can add User.books as a many-to-many, using your UserBook as the through table). 旁注,看多对多通过一些额外的好处(您可以将User.books添加为多对多,使用您的UserBook作为through表)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM