Django 在 ManyTomany 上过滤模型计数?

假设我在 models.py 中有类似的东西:

class Hipster(models.Model):
name = CharField(max_length=50)


class Party(models.Model):
organiser = models.ForeignKey()
participants = models.ManyToManyField(Profile, related_name="participants")

现在,在 views.py 中,我希望执行一个查询,为参与者超过0的用户获取一个方。

大概是这样的:

user = Hipster.get(pk=1)
hip_parties = Party.objects.filter(organiser=user, len(participants) > 0)

最好的方法是什么?

24888 次浏览

If this works this is how I would do it.

Best way can mean a lot of things: best performance, most maintainable, etc. Therefore I will not say this is the best way, but I like to stick to the ORM features as much as possible since it seems more maintainable.

from django.db.models import Count


user = Hipster.objects.get(pk=1)
hip_parties = (Party.objects.annotate(num_participants=Count('participants'))
.filter(organiser=user, num_participants__gt=0))
Party.objects.filter(organizer=user, participants__isnull=False)
Party.objects.filter(organizer=user, participants=None)

Easier with exclude:

# organized by user and has more than 0 participants
Party.objects.filter(organizer=user).exclude(participants=None)

Also returns distinct results

Derived from @Yuji-'Tomita'-Tomita answer, I've also added .distinct('id') to exclude the duplitate records:

Party.objects.filter(organizer=user, participants__isnull=False).distinct('id')

Therefore, each party is listed only once.

I use the following method when trying to return a queryset having at least one object in a manytomany field: First, return all the possible manytomany objects:

profiles = Profile.objects.all()

Next, filter the model by returning only the queryset containing at least one of the profiles:

hid_parties = Party.objects.filter(profiles__in=profiles)

To do the above in a single line:

hid_parties = Party.objects.filter(profiles__in=Profile.objects.all())

You can further refine individual querysets the normal way for more specific filtering.
NOTE:This may not be the most effective way, but at least it works for me.