Django: проверка по модели «многие ко многим через» - PullRequest
1 голос
/ 21 сентября 2010

У меня есть следующие модели (упрощенный пример):

class Book(models.Model):
 users = models.ManyToManyField(User, through=Permission)

class Permission(models.Model):
 user = models.ForeignKey(User)
 role = models.ForeignKey(Group)
 active = models.BooleanField()
 book = models.ForeignKey(Book)

Что мне нужно, так это то, что для экземпляра Book не может быть более одного пользователя с одинаковой ролью и активностью. Так что это разрешено:

Alice, Admin, False (not active), BookA
Dick, Admin, True (active), BookA
Chris, Editor, False (not active), BookA
Matt, Editor, False (not active), BookA

Но это не разрешено:

Alice, Admin, True (active), BookA
Dick, Admin, True (active), BookA

Теперь это не может быть сделано с unique_together, потому что это считается только тогда, когда активным является True. Я пытался написать собственный метод очистки (например, как я сделал здесь ). Но кажется, что когда вы сохраняете книгу и она запускает проверку для каждого разрешения, уже проверенные экземпляры разрешения не сохраняются, пока все они не будут проверены. Это имеет смысл, потому что вы не хотите, чтобы они были сохранены на случай, если что-то не подтвердится.

Может кто-нибудь сказать мне, если есть способ выполнить проверку, описанную выше?

P.S. Я мог бы представить себе использование функции сохранения (http://docs.djangoproject.com/en/1.2/topics/db/transactions/),, но я действительно хочу рассматривать это как последнее средство.
Может быть, вы можете сделать что-то вроде: unique_together = [[book, role, active=1],]?

Редактировать 23 сентября 2010 г. 14:00 Ответ Маноджу Говиндану:

Мой admin.py (упрощенная версия для ясности):

class BookAdmin(admin.ModelAdmin):
    inlines = (PermissionInline,)

class PermissionInline(admin.TabularInline):
    model = Permission

В оболочке ваша проверка работала бы. Поскольку сначала необходимо создать экземпляр книги, а затем создать все экземпляры Разрешения по одному: http://docs.djangoproject.com/en/1.2/topics/db/models/#extra-fields-on-many-to-many-relationships. Таким образом, в оболочке, если вы добавляете 2 экземпляра Разрешения, 1-й экземпляр Разрешения был сохранен к моменту, когда 2-й будет проверено, и поэтому проверка работает.

Однако, когда вы используете интерфейс администратора и одновременно добавляете все экземпляры book.users через встроенных пользователей книги, я считаю, что он сначала выполняет всю проверку всех экземпляров book.users, а затем сохраняет их. Когда я попробовал это сделать, проверка не сработала, просто она прошла успешно без ошибки, когда должна была быть ошибка ValidationError.

Ответы [ 3 ]

1 голос
/ 06 февраля 2012

Вы можете использовать сигналы, чтобы предотвратить сохранение недопустимых данных: я все еще работаю над хорошим решением о том, как сделать так, чтобы проверка выглядела в администраторе.

@receiver(models.signals.m2m_changed, sender=Book.users.through)
def prevent_duplicate_active_user(sender, instance, action, reverse, model, pk_set, **kwargs):
    if action != "pre_add":
        return
    if reverse:
        # Editing the Permission, not the Book.
        pass
    else:
        # At this point, look for already saved Users with the book/active.
        if instance.permissions.filter(active=True).exists():
            raise forms.ValidationError(...)

Обратите внимание, что это не полное решение, а указатель того, как я делаю нечто подобное.

0 голосов
/ 25 ноября 2010

Теперь это не может быть сделано с unique_together, потому что он считается только тогда, когда активным является True.

Самый простой способ, imo, это изменить тип active с BooleanField на CharField. Сохраните «Y» и «N» в active. Таким образом, вы можете использовать встроенный unique_together = [[book, role, active],]

0 голосов
/ 22 сентября 2010

Один из способов сделать это - использовать новомодную модель проверки .В частности, вы можете добавить пользовательский метод validate_unique к Permission моделям для достижения этого эффекта.Например,

from django.core.exceptions import ValidationError, NON_FIELD_ERRORS

class Permission(models.Model):
    ...
    def validate_unique(self, exclude = None):
        options = dict(book = self.book, role = self.role, active = True)
        if Permission.objects.filter(**options).count() != 0:
            template = """There cannot be more than one User of with the
                same Role and Active (book: {0})"""
            message = template.format(self.book)
            raise ValidationError({NON_FIELD_ERRORS: [message]})

Я провел элементарное тестирование, используя приложение администратора моего проекта, и оно, похоже, сработало.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...