How to query multiple described objects exist in a many to many field

112 views
Skip to first unread message

Aamu Padi

unread,
Dec 4, 2013, 5:24:56 AM12/4/13
to django...@googlegroups.com
How do I check whether there is a thread containing only the sender (user 1) and the recipient (user 2), and no other users.

models.py

    class Thread(models.Model):
        user = models.ManyToManyField(User)
        is_hidden = models.ManyToManyField(User, related_name='hidden_thread', blank=True)

    class Message(models.Model):
        thread = models.ForeignKey(Thread)
        sent_date = models.DateTimeField(default=datetime.now)
        sender = models.ForeignKey(User)
        body = models.TextField()
        is_hidden = models.ManyToManyField(User, related_name='hidden_message', blank=True)

I tried

    >>> Thread.objects.filter(user=user1&user2)
    >>> Thread.objects.filter(user=user1|user2)
    # Both gave me an error:  Unsupported operand.

    # Then this
    >>> Thread.objects.filter(Q(user=user1) & Q(user=user2))
    # Which gave me no threads at all.

    # Then this
    >>> Thread.objects.filter(Q(user=user1) | Q(user=user2)).distinct()
    # Gave me threads of both the users.

What I want is to check the thread, with the specified users only. Suppose, User 1 wants to send a message to User 2. What I want is, first check whether there is a thread between both the users. If there is, get that thread, or else create a new one. How is it possible? What is the best way to do it. Please help me. Thank you.

And please tell me what's the difference between | and &? Because I had very different results with these two.

Tom Evans

unread,
Dec 4, 2013, 7:11:38 AM12/4/13
to django...@googlegroups.com
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

Aamu Padi

unread,
Dec 5, 2013, 5:29:28 PM12/5/13
to django...@googlegroups.com
Thank you so much! That was a very helpful explanation. And it worked perfectly!!!

Just, one more question please. Suppose, the user wants to send a message to a group of users, how do I find the thread in this situation?

Eg: user1 wants to send a message to both the user2 and user3. How to find the thread, in this case 't3'?

>>> t1 = Thread.objects.get(id=1)
>>> t1
[<User: user1>,<User: user3>]
>>> t2 = Thread.objects.get(id=2)
>>> t2
[<User: user1>,<User: user2>]
>>> t3 = Thread.objects.get(id=3)
>>> t3
[<User: user1>,<User: user2>,<User: user3>]



Tom

--
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-users...@googlegroups.com.
To post to this group, send email to django...@googlegroups.com.
Visit this group at http://groups.google.com/group/django-users.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/CAFHbX1J%3DWX9p12B2X%2B1b_afDF0yEr80t0cfBsGbggzrKEBqqTg%40mail.gmail.com.
For more options, visit https://groups.google.com/groups/opt_out.

Reply all
Reply to author
Forward
0 new messages