3

Lets say tat I have such a model:

class Conversation(models.Model):
    sid = models.CharField(
        max_length=34,
        primary_key=True
    )
    members = models.ManyToManyField(to=User)

I would like to find a Conversation that is connected exactly/only with users [1,2]

I tried this:

conversation = Conversation.objects.filter(
            members__in=[1, 2]
        )

But it returns all conversation that are connected with either user 1, or 2.

I tried this: other answer but It returns empty queryset.

Przemek
  • 647
  • 2
  • 8
  • 25

1 Answers1

4

We can count how many members match the list, and check if that includes all the members:

from django.db.models import Count, Q

members = [1,2]
members_len = len(set(members))

Conversation.objects.annotate(
    total_members=Count('members'),
    matching_members=Count('members', filter=Q(members__in=members))
).filter(
    matching_members=members_len,
    total_members=members_len
)

This thus will retrieve Converstions that contain all members in the members list, and only these members (so not a superset, nor a subset).

Or for pre installations:

from django.db.models import Case, Count, When

members = [1,2]
members_len = len(set(members))

Conversation.objects.annotate(
    total_members=Count('members'),
    matching_members=Count(Case(
        When(members__in=members, then='members'),
        default=None
    ))
).filter(
    matching_members=members_len,
    total_members=members_len
)
lueho
  • 186
  • 11
Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555