I hope you understand SQL a little, as it will be required to follow
this explanation.
The condition "Thread.objects.filter(user=..)" goes across a join to
the users<->threads "through" table, which has columns (id, thread_id,
user_id).
When you specify conditions in a "filter()" call, each of the
conditions specified in that call is AND'ed together (the equivalent
of what you were doing in the "Q(user=user1) & Q(user=user2)", django
would do that for you). However, what does that mean, in terms of SQL?
Well, it means "find me rows from the users<->threads through table
that have user_id=
user1.pk and user_id=
user2.pk". Obviously, this will
never find results unless user1==user2.
So, you need to specify that you want to join twice to that table. You
do this by specifying the conditions for each join in a separate
"filter()" call, and so to find threads that have a row with user1 in
the through table, and also have user2 in the through table:
Thread.objects.filter(user=user1).filter(user=user2)
You started by asking how to find threads which had user1 and user2,
but no other users. This is more complex, you need to count the number
of users in the thread without filtering the users first! This means
two queries, or a sub query:
threads_with_both = Thread.objects.filter(user=user1).filter(user=user2)
threads_with_only_both = Thread.objects.filter(
pk__in=threads_with_both).annotate(
num_users=Count('user')).filter(
num_users=2)
>
> And please tell me what's the difference between | and &? Because I had very
> different results with these two.
| is OR
& is AND
So to find threads with either user1 or user2 (or both):
Thread.objects.filter(Q(user=user1) | Q(user=user2))
To find hidden threads with user1
Thread.objects.filter(Q(user=user1) & Q(is_hidden=True))
which is the same as
Thread.objects.filter(user=user1, is_hidden=True)
Cheers
Tom