Django - 强制ManyToManyField唯一项

36 投票
2 回答
21430 浏览
提问于 2025-04-16 11:04

我想做一些简单的事情,比如这样:

members = models.ManyToManyField(User, blank=True, null=True, unique=True)

但是不允许设置唯一性。当我查看创建的表时,发现它生成了外键,所以我想唯一性是隐含的。

我想把成员和这个代表小组的模型关联起来。这个小组可以没有成员,但我不想让同一个成员能加入同一个小组两次。

我原本以为如果我尝试这样做会抛出一个异常,但似乎并没有抛出异常。

def join(request,id):
    user = request.user
    mygroup = Group.objects.get(id=id)
    mygroup.members.add(user)
    mygroup.num_members += 1
    mygroup.save()

因为没有抛出异常,所以num_members在增加。重复的用户在管理工具中没有出现。难道add()默默失败了吗?我是不是应该在添加之前先检查一下用户是否已经存在?

2 个回答

17

重复的用户在管理工具中不会出现。

因为这些用户根本没有被创建。

add() 方法会悄悄失败吗?

是的。

我是不是应该在添加用户之前先检查一下这个用户是否已经存在?

没错。或者,你也可以让数据库帮你统计用户数量,而不是自己手动去数:

mygroup = Group.objects.filter(...).annotate(num_members=models.Count("members"))

这样就不需要在模型里再加一个 num_members 字段了。

另外,你在函数里不应该用 id 作为参数名。

43

首先,我不建议使用 num_members。你可以通过 mygroup.members.count() 来查看有多少个成员。其次,添加成员多次并不会真的重复添加,所以这点你不用担心。

Group 中使用 ManyToManyField 来指向 User,实际上是通过一个单独的表来实现的(类似于 group_group_users)。这个表里有指向 GroupUser 的外键。一个用户可以加入多个小组,而一个小组也可以有多个用户,但在 group_group_users 表中,不能有两行记录表示同样的关系(也就是说,外键组合必须是唯一的)。

用法:

>>> group = Group.objects.get(pk=1)
>>> user = User.objects.get(pk=1)
>>> group.members.add(user)
>>> # Worked fine as expected. Let's check the results.
>>> group.members.all()
[<User: foousername>]
>>> group.members.add(user)
>>> # Worked fine again. Let's check for duplicates.
>>> group.members.all()
[<User: foousername>]
>>> # Worked fine.

撰写回答