13👍
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.
12👍
You haven’t shown any code, so you haven’t really explained what the problem is:
Start with the queryset Book.objects.all()
. For each filter, check if there is a value for the filter in request.POST
, and if so, filter the queryset. Django querysets are lazy, so only the final queryset will be evaluated.
queryset = Book.objects.all()
if request.POST.get('age'):
queryset = queryset.filter(author__age=request.POST['age'])
if request.POST.get('gender'):
queryset = queryset.filter(author__gender=request.POST['gender'])
...
5👍
You can simply get the request.GET content as a dict (making sure to convert the values to string or a desired type as they’d be list by default i.e: dict(request.GET)
would give you something like {u'a': [u'val']}
.
Once you are sure you have a dictionary of keys matching your model fields, you can simply do:
filtered = queryset.filter(**dict_container)
- Django, phpmyadmin and mysql?
- Django: Save user uploads in seperate folders
- Can you find out if a Django Model instance is "dirty"?
3👍
this worked for me, I’ve merged Alex Morozov answer with Dima answer
import operator
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
- Django – annotate() – Sum() of a column with filter on another column
- Django rest framework cache policy
- In Django loaddata it throws errors for json format but work properly for yaml format, why?
- Django RequestFactory file upload
2👍
Maybe django-filter would help simplify the solutions others have given?
Something like:
class BookFilter(django_filters.FilterSet):
class Meta:
model = Book
fields = ['author__age', 'author__gender', ...]
Then the view looks like:
def book_list(request):
f = BookFilter(request.GET, queryset=Book.objects.all())
return render_to_response('my_app/template.html', {'filter': f})
For more information see the documentation.
- Exposing django admin to users. Harmful?
- Avoid recursive save() when using celery to update Django model fields
- Django Heroku Server Error (500) when I set Debug – False on True it is working fine
- CSRF is only checked when authenticated in DRF?
1👍
You can do something like that
class BooksAPI(viewsets.ModelViewSet):
queryset = Books.objects.none()
def get_queryset(self):
argumentos = {}
if self.request.query_params.get('age'):
argumentos['age'] = self.request.query_params.get('age')
if self.request.query_params.get('gender'):
argumentos['gender'] = self.request.query_params.get('gender')
if len(argumentos) > 0:
books = Books.objects.filter(**argumentos)
else:
books = Books.objects.all()
return books
- Django – multiple pluralization in admin model
- How to pass the remote IP to a proxied service? – Nginx
- Django: Annotate based on an annotation
- Can i access the response context of a view tested without the test client?
- Django: AttributeError: 'NoneType' object has no attribute 'split'
1👍
For a very simple equality check, here is my solution using a helper function in a ModelViewSet.
-
The helper function
check_for_params
creates a dictionary of request parameters passed in the URL. -
Alter the ModelViewSet
get_queryset()
method by filtering the Django QuerySet with a single filter clause which prevents multiple queries being called by chaining filters.
I could tried to use the Django Q() object but could not get it to only make a single call.
def check_for_params(request, param_check_list: List) -> dict:
"""
Create a dictionary of params passed through URL.
Parameters
----------
request - DRF Request object.
param_check_list - List of params potentially passed in the url.
"""
if not param_check_list:
print("No param_check_list passed.")
else:
param_dict = {}
for p in param_check_list:
param_dict[p] = request.query_params.get(p, None)
return param_dict
class MyModelViewSet(viewsets.ModelViewSet):
queryset = MyModel.objects.all()
serializer_class = MyModelSerializer
authentication_classes = [SessionAuthentication]
permission_classes = [IsAuthenticated]
def get_queryset(self):
"""
Return a queryset and apply filters, if applicable.
Info
----
Building the queryset.filter method by unpacking the key-value pairs this way,
creates a single filter clause and prevents multiple queries from being called
by chaining filters.
"""
queryset = MyModel.objects.all()
param_check_list = ['param1', 'param2', 'param3']
params = check_for_params(self.request, param_check_list)
filtered = {k: v for k, v in params.items() if v}
# Calling filter() only once here prevents multiple queries.
return queryset.filter(**filtered)