Vassily
Vassily

Reputation: 5428

How to filter queryset by two lookups of the same field?

I'm building a tinder-like app. Here is a model represents review from one user to another:

class Like(models.Model):
    like_from = models.ForeignKey(CustomUser, on_delete=models.CASCADE, related_name='outcome_likes')
    like_to = models.ForeignKey(CustomUser, on_delete=models.CASCADE, related_name='income_likes')
    is_positive = models.BooleanField()

created_at = models.DateTimeField(auto_now_add=True)

def __str__(self):
    return f'{self.like_from} -> {self.like_to}'

Now I'm trying to filter all matches and all I have is this code:

def get_queryset(self):
    return CustomUser.objects.filter(income_likes__like_from=self.request.user, income_likes__is_positive=True)\
        .filter(outcome_likes__like_to=self.request.user, outcome_likes__is_positive=True)
    # I also tried to go from the opposite side but 
    # also had no idea how to get correct solution
    # Here should be some code to get intersection 
    # of these two querysets's values lists
    # positive_likes_from = self.request.user.income_likes.all().filter(is_positive=True).values('like_from')
    # positive_likes_to = self.request.user.outcome_likes.all().filter(is_positive=True).values('like_to')

But uncommented line here will return users that have any positive outcome likes with no guarantee that they will be addressed to the current user. I want to get a queryset of CustomUser model that have positive income and outcome likes with a current user on another side. Here is an solution of my problem that requires tons of SQL queries to the database:

def get_queryset(self):
    positive_likes_from = self.request.user.income_likes.all().filter(is_positive=True)
    positive_likes_to = self.request.user.outcome_likes.all().filter(is_positive=True)
    result = []
    for like in positive_likes_from:
        outcome_like_with_same_user_on_another_side = positive_likes_to.filter(like_to=like.like_from).first()
        if outcome_like_with_same_user_on_another_side:
            result.append((like, outcome_like_with_same_user_on_another_side))
    return result

Upvotes: 0

Views: 549

Answers (1)

dirkgroten
dirkgroten

Reputation: 20672

You can intersect two QuerySets using the intersection function or the & operator (a QuerySet is a python set) as described here.

So if you create two QuerySets for the outgoing likes and incoming likes:

qs1 = self.request.user.income_likes.filter(is_positive=True).values_list('like_from', flat=True)
qs2 = self.request.user.outcome_likes.filter(is_positive=True).values_list('likes_to', flat=True)

you'll have two lists of user_ids of which the intersection are the users that match. The flat=True is required to make two lists otherwise the keys in each list would be different and the intersection empty:

matches = CustomUser.objects.filter(id__in=qs1.intersection(qs2))

or if you just want the list of ids:

match_ids = qs1 & qs2

This gives you all the users that have a match with the request.user.

Upvotes: 2

Related Questions