Nanor
Nanor

Reputation: 2550

Query with multiple ManyToMany conditions

Suppose I have a list of skills:

skills = ['Python', 'Django', 'Java']

Suppose I then perform

Freelancer.object.filter(Q(skills=skills[0]))

Both Freelancers have this skill but despite the query not being the entirety of their repertoire I still want them to appear, which they will. I perform:

Freelancer.object.filter(Q(skills=skills[0]) | Q(skills=skills[1]) | Q(skills=skills[3]))

Freelancer one has all three skills, so I want him to appear. Freelancer two only matches 2/3 so I don't want him to appear but he obviously will because of the query. How can I do this?

Suppose this is possible with Q and skills is a list of size n. Is it possible to perform this query dynamically?

# models.py

class Freelancer(models.Model):
    def __unicode__(self):
        return self.first_name

    user = models.OneToOneField(User)
    first_name = models.CharField(max_length=128)
    surname = models.CharField(max_length=128)
    university = models.CharField(max_length=256)
    biography = models.TextField(default="")
    skills = models.ManyToManyField(Skill, blank=True)
    profile_picture = models.ImageField(blank=True, upload_to='freelancer/images')

    object = UserManager()


class Skill(models.Model):
    skill = models.TextField(primary_key=True, max_length=128)

    def __unicode__(self):
        return self.skill

EDIT:

For clarity I've provided the POST part of the view

# views.py

    form = FilterFreelancerForm(request.POST)
    skills = request.POST.getlist('skills_select')

    queries = [Q(skills=s) for s in skills]
    freelancers = Freelancer.object.filter(*queries)

Upvotes: 2

Views: 57

Answers (2)

Sede
Sede

Reputation: 61225

You need to use & (AND) instead of | (OR)

skills = ['Python', 'Django', 'Java']
queries = [Q(skill=s) for s in skills]
Freelancer.Object.filter(*queries)

or

Freelancer.Object.filter(Q(skills=skills[0]) & Q(skills=skills[1]) & Q(skills=skills[3]))

Upvotes: 1

Maciek
Maciek

Reputation: 3234

You need to call filter multiple times, because inside one filter, there can be only one JOIN to the Skills table. Source: https://docs.djangoproject.com/en/1.8/topics/db/queries/#spanning-multi-valued-relationships

So why not try it this way:

freelancers = Freelancer.objects.all()

for skill in skills:
    freelancers = freelancers.filter(skills__skill=skill)

print(list(freelancers))

Upvotes: 1

Related Questions