Есть ли более элегантный способ добавить уникальное ограничение вместе с учетом значений в модели Django?

Вот в чем проблема:

У меня такая модель:

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 (Истина).

В настоящее время я решаю эту проблему, изменив модель на такую:

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 (в моем случае не о чем беспокоиться, но иногда поле key может быть очень длинным).

Итак, я хотел бы знать, есть ли более элегантный подход к решению такого рода проблем.

Спасибо!


person Johnny Zhao    schedule 10.05.2013    source источник


Ответы (3)


Переопределите is_active следующим образом:

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

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

person FMc    schedule 10.05.2013

В Django 2.2 (в настоящее время выпущена как beta1) вы сможете использовать UniqueConstraint, которому в дополнение к списку fields может быть передано condition

Объект Q, определяющий условие, которое должно выполняться ограничением.

Например, UniqueConstraint (fields = ['user'], condition = Q (status = 'DRAFT') гарантирует, что у каждого пользователя будет только один черновик.

person Nour Wolf    schedule 15.03.2019

Основываясь на ответе Нура, вы можете сделать это:

class Meta:
    constraints = [
        models.UniqueConstraint(
            fields=['user'],
            condition=Q(is_active=True),
            name='unique active user book per user'
        ),
    ]
person Anthony    schedule 27.01.2020
comment
К сожалению, я получаю странную ошибку could not create unique index 'unique user' as key(user, is_active)=(6,f) is duplicated.. Я устанавливаю is_active = True, но также проверяю неактивных пользователей. - person Pran Kumar Sarkar; 08.09.2020