我有以下模型(简化示例):
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实例,不能有多个具有相同Role和Active的User。 所以这是允许的:
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完成此操作,因为它仅在active为True时计数。我试着写一个自定义的干净方法(就像我做的here)。但是,当您保存Book并且它在每个Permission上运行验证时,似乎已经验证的Permission实例在它们全部经过验证之前不会保存。这是有道理的,因为你不希望它们被保存以防万一没有验证。
有人能告诉我是否有办法执行上述验证?
P.S。我可以想象使用保存点功能(http://docs.djangoproject.com/en/1.2/topics/db/transactions/),但我真的只想将其视为最后的手段。
也许你可以这样做:unique_together = [[book, role, active=1],]
?
编辑2010年9月23日14:00回应Manoj Govindan:
我的admin.py(为了清晰起见,简化版):
class BookAdmin(admin.ModelAdmin):
inlines = (PermissionInline,)
class PermissionInline(admin.TabularInline):
model = Permission
在shell中,您的验证将起作用。因为您首先必须创建工作簿实例,然后逐个创建所有权限实例:http://docs.djangoproject.com/en/1.2/topics/db/models/#extra-fields-on-many-to-many-relationships。因此,在shell中,如果添加2个Permission实例,则第2个Permission实例在第2个验证时已经保存,因此验证工作正常。
但是,当您使用Admin界面并通过书籍用户内联同时添加所有book.users实例时,我相信它会在保存所有book.users实例之前先对所有book.users实例进行所有验证。 当我尝试它时,验证不起作用,它只是在应该存在ValidationError时成功而没有错误。
答案 0 :(得分:1)
您可以使用信号来阻止无效数据的保存:我仍在研究如何在管理员中以一种很好的方式使验证成为现实。
@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(...)
请注意,这不是一个完整的解决方案,而是指示我如何做类似的事情。
答案 1 :(得分:0)
一种方法是使用新奇的model validation。具体来说,您可以向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]})
我使用我的一个项目的管理应用程序进行了一些基本测试,它似乎有效。
答案 2 :(得分:0)
现在无法使用unique_together完成此操作,因为它仅在active为True时计数。
最简单的方法是将active
的类型从BooleanField更改为CharField。在'{1}}中存储'Y'和'N'。
这样您就可以使用内置的active