82

I need to perform a django query that checks if a field contains all values within a list. The list will be of varying length

Example

User.objects.filter(first_name__contains=['x','y','z'])

8 Answers 8

137
import operator
from django.db.models import Q
from functools import reduce

User.objects.filter(
    reduce(operator.and_, (Q(first_name__contains=x) for x in ['x', 'y', 'z']))
)

For python 2 you did not need to import reduce.

Sign up to request clarification or add additional context in comments.

14 Comments

User.objects.filter(reduce(...)) He is doing the equivalent of User.objects.filter(Q(first_name__contains='x') & Q(first_name__contains='y') & Q(first_name__contains='z'))
what is operator? Do I need to import it?
@IgnacioVazquez-Abrams If you wanted to use this within a for loop, is there any way to replace first_name with a variable that changes on each iteration (first_name, last_name etc) while keeping the __contains? Right now the only way I can see to run this against multiple fields is repeating the entire function and hardcoding the different field names.
This line of code is very precious, but lack of any explanation makes this a terrible answer.
|
40
import operator
from django.db.models import Q
from functools import reduce
    
q = ['x', 'y', 'z']
query = reduce(operator.and_, (Q(first_name__contains = item) for item in q))
result = User.objects.filter(query)

3 Comments

Hey :). Welcome to SO. Mind adding a bit of meat to your answer? We normally like to provide a bit more info than a simple code-dump for an answer, just to help other users better understand what's happening :). Thank you ^^
@Patrice Might as well make the same comment to the chosen answer, by the guy with 339K rep. This answer has more context at least. :)
if using Python 3, use import functools and use functools.reduce. See this
7

More readable solution.

qs = User.objects.all()
for search_term in ('x', 'y', 'z'):
    qs = qs.filter(first_name__contains=search_term) 

Note: Querysets are lazy, so this code makes 1 DB query.

3 Comments

Yeah, this works well for AND (&) queries. For OR (|) queries, Q objects must be used.
I ended up using this same approach. I think this solution is actually much more concise and much more readable than chaining multiple Qs in a line. And it suits op's problem well.
@Brandon yes but as @xyres said, for OR operator, you have to use Q objects
2

The accepted solution didn't work for me, but this did:

list = ['x', 'y', 'z']
results = User.objects.filter(first_name__contains=list[0])
del list[0]

for l in list:
    results = results.filter(first_name__contains=l)

The first results variable will store a list of all the objects with the first_name name field value 'x'.

And then in the for loop you filter for 'y' amongst the first filter results. Now you have a QuerySet that should contain a list of items where both 'x' and 'y' can be found. Now amongst those you filter for any item that contains 'z' as well.

This should work for any length list.

1 Comment

This works fine. You can even lose del statement, by simply slicing the list, i.e., for l in list[1:]:
1
from django.db.models import Q
User.objects.filter(Q(first_name__contains=x)&Q(first_name__contains=y)&Q(first_name__contains=z))

Works for me on Django 1.8 python 2.7

doc => https://docs.djangoproject.com/en/1.8/ref/models/querysets/#q-objects

for more recent => https://docs.djangoproject.com/en/2.1/ref/models/querysets/#q-objects

Comments

1
the_list= []

data_list= ['x', 'y', 'z'] 

for i in data_list:

    a = User.objects.filter(first_name__contains=project).values('etc')

    the_list+= a

1 Comment

As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.
0

This worked for me in django 2.2, python 3.8, using lambda instead of 'operator'. Explanations on lambda can be found here: https://www.python-course.eu/lambda.php

from functools import reduce
from django.db.models import Q

my_list = ['x','y','z']
User.objects.filter(reduce(lambda x, y: x & y, [Q(first_name__contains= i for i in my_list]))

Comments

0

It doesn't apply exactly here, but, if you have a ManyToManyField and you want to check if it contains a specific value, check this https://www.revsys.com/tidbits/tips-using-djangos-manytomanyfield/.

I have a "Products" and Django native "Users" model. My "Products" model has a many-to-many field users pointing to "Users". Then, I wanted to check if this list-like field contained the logged in user. I did that by ...users__username_icontains=request.user.username... and the link above helped me to understand better what is a many-to-many field and how it works.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.