My goal is to return rows from the books table using values sent by a user through a search form. The search form contains multiple fields author, ISBN and title.
NB: In the code, the author field is a char, but converted to ID based on authors name due to foreign key relation.
In this code, there's a get request converted to Dictionary and transformed to allow it to query data from the books table.
def search(request):
form = BooksForm()
if request.method == 'GET':
# convert get request to dictionary
book_search = dict(request.GET)
print("data before cleaning", book_search)
#get the name from the dictionary get request
author_name =request.GET['author']
#get the author id from the author table
author = Author.objects.filter(full_name__contains=author_name).values()
#get author_id from author queryset
author_id = author[0]['id']
#update the dict with the author id to allow search in books models (foreing key)
book_search['author'] = author_id
print("data after cleaning",book_search)
result_set = Book.objects.filter(**book_search)
print("book search queryset",result_set)
Problem: When I query the books table using the dictionary it returns an empty query set and I want it to return books from in table. Does anyone have an idea of how I can make the code work?
CodePudding user response:
Please don't allow to filter on arbitrary keys. This makes the view sensitive to request forgery where a person can for example make a request with author__gender
as key, and F
as value, and thus can expose data that you might want to hide.
You can use a library like django-filter
[GitHub] to determine how to filter the queryset. Here you can make a filter that looks like:
import django_filters
class BookFilter(django_filters.FilterSet):
author = django_filters.CharFilter(
field_name='author__full_name',
lookup_expr='contains'
)
class Meta:
model = Book
fields = ['isbn']
With this filter, it will only use the author
and isbn
keys of the request.GET
, and thus ignore other keys that might want to filter on sensitive data.
You then can use this with:
def search(request):
book_filter = ProductFilter(request.GET)
print('book search queryset', book_filter.qs)
return render(request, 'name-of-template.html', {'book_filter': book_filter})
In the template you can then use {{ book_filter.form }}
for a form based on the filter, and {% for item in book_filter.qs %} … {% endfor %}
to enumerate over the items in the filtered queryset.