4
class Publication(models.Model):
    title = models.CharField(max_length=30)


class Article(models.Model):
    headline = models.CharField(max_length=100)
    publications = models.ManyToManyField(Publication)

p1 = Publication.objects.create(title='The Python Journal')
p2 = Publication.objects.create(title='Science News')
p3 = Publication.objects.create(title='Science Weekly')

I want to filter articles, published in both p1 and p3. They might or might not publish in other publications but they must have to publish in p1 and p3.

I have tried:

Article.objects.filter(Q(publications=p1) & Q(publications=p3))

but it returns empty queryset which is not true

Tanvir Ahmed
  • 357
  • 2
  • 15

3 Answers3

3

If you chain mutliple calls to filter() they should behave like being connected with an AND:

articles = Article.objects.filter(publications=p1).filter(publications=p3).distinct()
Bernhard Vallant
  • 49,468
  • 20
  • 120
  • 148
  • Is there a way to do it using ``Q`` in a single filter – Tanvir Ahmed Jun 20 '22 at 13:41
  • 1
    No if you try it in one `filter()` Django will apply the condition to the same related object which will result in zero results. if you do two seperate filters it willl join the related table two times and filter for two objects. – Bernhard Vallant Jun 20 '22 at 14:01
1

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:

# filtering on a few publications, by id
articles = Article.objects.filter(publications__in=[<id1>, <id2>, <id3>])
# and by publication object (object gets converted to pk under the covers)
articles = Article.objects.filter(publications__in=[publication1, publication2, publication3])
# and by publication title
articles = Article.objects.filter(publications__title__icontains='Science')

The icontains used to i for case-insensitive, contains for that the word contains those letters and maybe there are fewer or more letters.

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

Ashrof
  • 146
  • 4
0

I had a similar problem where some users ended with a free role and also a paid role, so I needed to select users that have two specific roles on the many to many field.

I did something like this, works like a charm

roles = [premium_role, default_role]

DiscordUser.objects.filter(guild=guild)
    .annotate(freemium=Count('roles', filter=Q(roles__in=roles)))
    .filter(freemium=2)
    .values('id')
gio
  • 36
  • 2