desperado
desperado

Reputation: 213

Django sort model objects by intersection count of a property with another QuerySet

I have a problem with Django querying. I searched through web but still couldn't find a solution to it.

I have a model called Question:

class Question(models.Model):
    ...
    tags = models.ManyToManyField(Tag, related_name='tagged_questions')
    ...

And I need to write a method which is as below:

def get_recommended_questions(request):
    ...
    interested_tags = user.interested_tags.all()
    recommended_questions_list = ... // I need help here

I need to get a list of Question objects, which are sorted by the intersection count of their tags with interested_tags

For example:

interested_tags = [A, B, C]

And I have following questions in the database:

q1.tags = [A, B, C] // intersection count = 3
q2.tags = [D] // intersection count = 0
q3.tags = [A, B, D] // intersection count = 2
q4.tags = [A, D] // intersection count = 1

What I want to get is:

recommended_questions_list = [q1, q3, q4]

I want to know if there is a way to do this without using raw SQL.

Thank you very much for your help!

Upvotes: 4

Views: 362

Answers (2)

Ritesh Agrawal
Ritesh Agrawal

Reputation: 821

In this, first we require to get all the questions that has any of the tag in which user is interested. Then we can sort it based on the total matched tags between user and question.

Sample code:

interested_tags_ids = user.interested_tags.all().values_list('id', flat=True)
questions = Question.objects.filter(tags__in=interested_tags_ids).prefetch_related('tags')
interested_tags_set = set(interested_tags_ids)

sort_func = lambda question: len(set(question.tags.all().values_list('id', flat=True)) & interested_tags_set)
recommended_questions_list = sorted(questions, key=sort_func, reverse=True)

Upvotes: 2

Arpit Solanki
Arpit Solanki

Reputation: 9931

That's long query so I will go step by step

def get_recommended_questions(request):
    ...
    interested_tags = user.interested_tags.all()
    questions = Question.objects.all()
    recommended_questions_list = [(question, 
             len(set(interested_tags)&set(list(question.tags.all())))) 
             for question in questions]
    # the above list has tuples with question object and intersection count

Now you can sort however you want

Upvotes: 1

Related Questions