Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django - enforcing ManyToManyField unique items

I'm trying to do something simple like this:

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

but unique isn't allowed. When looking at the table created, it makes foreign keys so uniqueness is implied I imagine.

I want to be able to associate members with this model representing a group. The group can have no members but I don't want the same member to be able to join the group twice.

My thought would be that an exception would be thrown if I try and do this but it seems that an exception is not thrown.

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 is incremented, because no exception is thrown. Duplicate users don't appear in the admin utility. Does add() fail silently? Should I just simply check if the user is contained already before adding?

like image 257
JPC Avatar asked Feb 02 '11 05:02

JPC


2 Answers

For one, I wouldn't use num_members. Instead, you can check how many members there are with mygroup.members.count(). Secondly, adding members more than once doesn't really add them more than once, so you're fine.

A ManyToManyField on Group for member pointing to User is implemented with a separate table (something like group_group_users) which has a foreign key to Group and User. A user can have multiple groups, and a group can have multiple users, but there can't be two rows in group_group_users for the same relationship (ie, unique together foreign keys).

Usage:

>>> 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.
like image 64
orokusaki Avatar answered Nov 15 '22 10:11

orokusaki


Duplicate users don't appear in the admin utility.

They're not created.

Does add() fail silently?

Yes.

Should I just simply check if the user is contained already before adding?

Yes. Or instead of manually counting the users, you could just have the database count for you:

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

This eliminates the need for a num_members field in the actual model.

Also, you shouldn't use id as a parameter name to your function.

like image 16
Mike DeSimone Avatar answered Nov 15 '22 10:11

Mike DeSimone