In Django, How Does One Filter a Queryset with Dynamic Field Lookups

How to dynamically provide lookup field name in Django query?

I think there may be a better way to do this with the Django query system. Here's how to do it your way.

Python allows you to pass dictionaries to be used as argument lists by prefixing them with **.
With a spot of luck, you should be able to do something like this:

lookup = "%s__contains" % field
results.append(Item.objects.filter(**{ lookup: keyword}))

In Django, how does one filter a QuerySet with dynamic field lookups?

Python's argument expansion may be used to solve this problem:

kwargs = {
'{0}__{1}'.format('name', 'startswith'): 'A',
'{0}__{1}'.format('name', 'endswith'): 'Z'
}

Person.objects.filter(**kwargs)

This is a very common and useful Python idiom.

How to dynamically filter with field name, condition and values in Django

After going through a few StackOverflow posts I figured out the logic. So below is the logic.

def get_filter(self, field_name, filter_condition, filter_value):
# thanks to the below post
# https://stackoverflow.com/questions/310732/in-django-how-does-one-filter-a-queryset-with-dynamic-field-lookups
# the idea to this below logic is very similar to that in the above mentioned post
if filter_condition.strip() == "contains":
kwargs = {
'{0}__icontains'.format(field_name): filter_value
}
return Q(**kwargs)

if filter_condition.strip() == "not_equal":
kwargs = {
'{0}__iexact'.format(field_name): filter_value
}
return ~Q(**kwargs)

if filter_condition.strip() == "starts_with":
kwargs = {
'{0}__istartswith'.format(field_name): filter_value
}
return Q(**kwargs)
if filter_condition.strip() == "equal":
kwargs = {
'{0}__iexact'.format(field_name): filter_value
}
return Q(**kwargs)

if filter_condition.strip() == "not_equal":
kwargs = {
'{0}__iexact'.format(field_name): filter_value
}
return ~Q(**kwargs)

def get(self, request):
# getting the array of objects data to filter. The array of objects data
# example is in the question
filter_data = request.query_params.getlist('filterData[]')

all_books = Books.objects.all()
# Creating initial Q object
filter_objects = Q()

# Looping through the array of objects
for data in filter_data:
# The main part. Calling get_filter and passing the filter object data.
filter_objects &= self.get_filter(
data["fieldName"], data["filterCondition"],
data["filterValue"])
filtered_data = all_books.filter(filter_objects)

Hope it helps someone. :)

django dynamically filtering with q objects

You'll want to loop through the tag_list and apply a filter for each one.

tag_list = ['tag1', 'tag2', 'tag3']
base_qs = Design.objects.all()
for t in tag_list:
base_qs = base_qs.filter(tags__tag__contains=t)

This will give you results matching all tags, as your example indicated with and. If in fact you needed or instead, you will probably need Q objects.

Edit: I think I have what you're looking for now.

tags = ['tag1', 'tag2', 'tag3']
q_objects = Q() # Create an empty Q object to start with
for t in tags:
q_objects |= Q(tags__tag__contains=t) # 'or' the Q objects together

designs = Design.objects.filter(q_objects)

I tested this and it seems to work really well.

Edit 2: Credit to kezabelle in #django on Freenode for the initial idea.

How to filter queryset with one lookup field on mutiple fields in django-filter

You don't need to use django-filter for that, what you actually need is SearchFilter

Your view should look something like this:

from rest_framework import filters

class PartnerViewSet(ModelViewSet):
""" Partner View Set """

serializer_class = PartnerSerializer
queryset = Partner.objects.select_related("user").all()
permission_classes = [IsAuthenticated,]
authentication_classes = [SessionAuthentication]
filter_backends = [filters.SearchFilter]
search_fields = ['id', 'name'] # add here any other field that you want to match your search

After adding this you can use your endpoint with .../?search=search_value

How to add filters to a query dynamically in Django?

Here's a bit more generic one. It will apply filters to your queryset if they are passed as the GET parameters. If you're doing a POST call, just change the name in the code.

import operator
from django.db.models import Q

def your_view(self, request, *args, **kwargs):
# Here you list all your filter names
filter_names = ('filter_one', 'filter_two', 'another_one', )

queryset = Books.objects.all();
filter_clauses = [Q(filter=request.GET[filter])
for filter in filter_names
if request.GET.get(filter)]
if filter_clauses:
queryset = queryset.filter(reduce(operator.and_, filter_clauses))

# rest of your view

Note that you can use lookup expressions in your filters' names. For example, if you want to filter books with price lower or equal to specified in filter, you could just use price__lte as a filter name.

How to create dynamic queries with Django Q objects from parenthesis inside a string

So I got it done using by changing the 'infix expression solver' a bit.

Here's the code:

def complex_filter_by_string(mystr):
"""
Filters string by using Q objects and infix expression solver.
The input string should be of the type ( name = abc OR name = xyz ) AND ( other = fgh ) Or ( other_one = xyz )
Notice the spaces after each 'word'. We are splitting the string by space, so space is necessary.
"""
tokens = mystr.split()

# stack to store integer values.
values = []

# stack to store operators.
ops = []
i = 0
while i < len(tokens):

# Current token is a whitespace,
# skip it.
if tokens[i] == ' ':
i += 1
continue

# Current token is an opening
# brace, push it to 'ops'
elif tokens[i] == '(':
ops.append(tokens[i])

# Current token is =, convert it to a Q object and push
# it to stack for q objects.
elif tokens[i] == "=":

# if token equals '=' . It means we will have tokens[i-1]=attribute and tokens[i+1]=value (this is a constraint on string)
obj = {}
key = tokens[i-1]
value = tokens[i+1]
if isinstance(value,str):
i+=1
while i+1 < len(tokens) and (tokens[i+1] != 'OR' and tokens[i+1] != 'AND' and tokens[i+1] != ')'):
value= value + " " + tokens[i+1]
i+=1
value = value.rstrip()
obj[key]=value
val = Q(**obj)
values.append(val)


# Closing brace encountered,
# solve entire brace.
elif tokens[i] == ')':

while len(ops) != 0 and ops[-1] != '(':

val2 = values.pop()
val1 = values.pop()
op = ops.pop()

values.append(applyOp(val1, val2, op))

# pop opening brace.
ops.pop()

# Current token is an operator.
elif tokens[i] == "OR" or tokens[i] == "AND":

# While top of 'ops' has same or
# greater precedence to current
# token, which is an operator.
# Apply operator on top of 'ops'
# to top two elements in values stack.
while (len(ops) != 0 and
precedence(ops[-1]) >=
precedence(tokens[i])):

val2 = values.pop()
val1 = values.pop()
op = ops.pop()

values.append(applyOp(val1, val2, op))

# Push current token to 'ops'.
ops.append(tokens[i])

i += 1

# Entire expression has been parsed
# at this point, apply remaining ops
# to remaining values.
while len(ops) != 0:

val2 = values.pop()
val1 = values.pop()
op = ops.pop()

values.append(applyOp(val1, val2, op))

# Top of 'values' contains result,
# return it.
return values[-1]

This will return a Q object which you can pass onto your model lie MyModel.objects.filter(q_obj)

There are a few restrictions on the string though. The main restriction is 'there should be a whitespace after each keyword'. A keyword can be ' OR , AND , attribute , value , = , open_parenthesis '.

So the string that i posted in question should be : ( name = abc OR name = xyz ) AND (status = active OR ( status = available AND age = 30 ))



Related Topics



Leave a reply



Submit