Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to filter django model by its objects in many-to-many field (exact match)?

I have this model in my code:

class Conversation(models.Model):
    participants = models.ManyToManyField(User, related_name="message_participants")

and I need to filter this "Conversation" model objects by the "participants" many-to-many field. meaning: I have for example 3 User objects, so I want to retrieve the only "Conversation" objects that has this 3 Users in it's "participants" field.

I tried doing this:

def get_exist_conv_or_none(sender,recipients):
    conv = Conversation.objects.filter(participants=sender)
    for rec in recipients:
        conv = conv.filter(participants=rec)

where sender is a User object and "recipients" is a list of User objects. it won't raise error but it gives me the wrong Object of Conversation. Thanks.

edit: A more recent try lead me to this:

def get_exist_conv_or_none(sender,recipients):
    participants=recipients
    participants.append(sender)
    conv = Conversation.objects.filter(participants__in=participants)
    return conv

which basically have the same problem. It yields Objects which has one or more of the "participants" on the list. but what Im looking for is exact match of the many-to-many object. Meaning, an Object with the exact "Users" on it's many-to-many relation.

edit 2: My last attempt. still, won't work.

def get_exist_conv_or_none(sender,recipients):
    recipients.append(sender)
    recipients = list(set(recipients))
    conv = Conversation.objects.annotate(count=Count('participants')).filter(participants=recipients[0])
    for participant in recipients[1:]:
        conv.filter(participants=participant)
    conv.filter(count=len(recipients))
    return conv
like image 748
SnirD Avatar asked Nov 27 '12 14:11

SnirD


People also ask

How take data from many-to-many field in Django?

A ManyToManyField in Django is a field that allows multiple objects to be stored. This is useful and applicable for things such as shopping carts, where a user can buy multiple products. To add an item to a ManyToManyField, we can use the add() function.

Can we use multiple filters in Django?

I always assumed that chaining multiple filter() calls in Django was always the same as collecting them in a single call. The first queryset with the chained filter() calls joins the Inventory model twice effectively creating an OR between the two conditions whereas the second queryset ANDs the two conditions together.

What does objects all () do in Django?

all() Returns a copy of the current QuerySet (or QuerySet subclass). This can be useful in situations where you might want to pass in either a model manager or a QuerySet and do further filtering on the result. After calling all() on either object, you'll definitely have a QuerySet to work with.

What is objects filter in Django?

The filter() method is used to filter you search, and allows you to return only the rows that matches the search term.


1 Answers

Ok so I found the answer: In order to make an exact match I have to chain-filter the model and then make sure it has the exact number of arguments it needs to have, so that the many-to-many field will have in it all the objects needed and no more.

I will check for the objects number using annotation: ( https://docs.djangoproject.com/en/dev/topics/db/aggregation/ )

ended up with this code:

def get_exist_conv_or_none(recipients):
    conv = Conversation.objects.annotate(count=Count('participants')).filter(participants=recipients[0])
    for participant in recipients[1:]:
        conv = conv.filter(participants=participant)
    conv = conv.filter(count=len(recipients))
    return conv
like image 114
SnirD Avatar answered Sep 17 '22 16:09

SnirD