开发者

Django ManyToMany filter()

开发者 https://www.devze.com 2022-12-19 05:32 出处:网络
I have a model: class Zone(models.Model): name = models.CharField(max_length=128) users = models.ManyToManyField(User, related_name=\'zones\', null=True, blank=True)

I have a model:

class Zone(models.Model):
    name = models.CharField(max_length=128)
    users = models.ManyToManyField(User, related_name='zones', null=True, blank=True)

And I need to contruct a filter along the lines of:

u = User.objects.filter(...zones contains a particular zone...)

It has to be a filter on User and it has to be a single filter parameter. The reason for this is that I am constructing a URL querystring to filter the admin user changelist: http://myserver/admin/auth/user/?zones=3

It seems like it should be simple b开发者_运维技巧ut my brain isn't cooperating!


Just restating what Tomasz said.

There are many examples of FOO__in=... style filters in the many-to-many and many-to-one tests. Here is syntax for your specific problem:

users_in_1zone = User.objects.filter(zones__id=<id1>)
# same thing but using in
users_in_1zone = User.objects.filter(zones__in=[<id1>])

# filtering on a few zones, by id
users_in_zones = User.objects.filter(zones__in=[<id1>, <id2>, <id3>])
# and by zone object (object gets converted to pk under the covers)
users_in_zones = User.objects.filter(zones__in=[zone1, zone2, zone3])

The double underscore (__) syntax is used all over the place when working with querysets.


Note that if the user may be in multiple zones used in the query, you may probably want to add .distinct(). Otherwise you get one user multiple times:

users_in_zones = User.objects.filter(zones__in=[zone1, zone2, zone3]).distinct()


another way to do this is by going through the intermediate table. I'd express this within the Django ORM like this:

UserZone = User.zones.through

# for a single zone
users_in_zone = User.objects.filter(
  id__in=UserZone.objects.filter(zone=zone1).values('user'))

# for multiple zones
users_in_zones = User.objects.filter(
  id__in=UserZone.objects.filter(zone__in=[zone1, zone2, zone3]).values('user'))

it would be nice if it didn't need the .values('user') specified, but Django (version 3.0.7) seems to need it.

the above code will end up generating SQL that looks something like:

SELECT * FROM users WHERE id IN (SELECT user_id FROM userzones WHERE zone_id IN (1,2,3))

which is nice because it doesn't have any intermediate joins that could cause duplicate users to be returned


You could use Q object if you have more complex query in the scenario like this question.

based on this question suppose you want to filter Users with specific id(e.g. 10) and filter Zones with the name that starts with 'europe'

query would be like this:

filtered_zone = Q(zones__in=Zone.objects.filter(name__startswith='europe')
result = User.objects.filter(Q(id=10) & Q(filtered_zone))
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号