Reputation: 5366
I have a model Book:
class Book(models.Model):
book_id = models.AutoField(primary_key=True)
title = models.CharField(max_length=30)
# A book can have multiple authors and an author can have multiple books
author = models.ManyToManyField('Author')
genre = models.CharField(max_length=20)
isbn = models.CharField(max_length=20)
summery = models.CharField(max_length=100, null=True)
language = models.CharField(max_length=20, null=True)
status = models.CharField(max_length=15, null=True)
number_of_pages = models.IntegerField(blank=True)
borrowed = models.DateField(auto_now=False, auto_now_add=False, null=True, blank=True)
loan_period = models.IntegerField(default=14)
due_date = models.DateField(auto_now=False, auto_now_add=False, null=True, blank=True)
# A library has many books
which_library = models.ForeignKey('Library', related_name='books', on_delete=models.CASCADE)
#This helps to print in admin interface
def __str__(self):
return u"%s" % (self.title)
I have one url that gives me all the books in a specific library:
urlpatterns = [
url(r'^books/(?P<library_id>[0-9]+)$', book_list),
]
My view that returns those book objects:
@api_view(['GET', 'POST'])
def book_list(request, library_id):
"""
List all books in a specific library, or create a new book for a specific library
"""
if request.method == 'GET':
books = Book.objects.filter(which_library=library_id)
print(books)
serializer = BookSerializer(books, many=True)
return Response(serializer.data)
My question is: can I and if yes how can I modify my url and view so that this complete list of books can be filtered for different query params to return those specific books i.e. if I hit url such as api/books/1?genre=Fiction&status=Available
should further filter the books satisfying these criteria? Is it a good practice to do so or writing separate urls and views for this purpose is advised?
Upvotes: 2
Views: 1408
Reputation: 2808
You can get the query urls from the GET query and then apply filters if they exist.
The values passed in the url query parameters are accessible in Django using the GET
attribute of the request
object.
Your code should look something like this:
@api_view(['GET', 'POST'])
def book_list(request, library_id):
"""
List all books in a specific library, or create a new book for a specific library
"""
if request.method == 'GET':
genre_query = request.GET.get('genre', None)
status_query = request.GET.get('status', None)
books = Book.objects.filter(which_library=library_id)
if genre_query:
books = books.filter(genre=genre_query)
if status_query:
books = books.filter(status=status_query)
print(books)
serializer = BookSerializer(books, many=True)
return Response(serializer.data)
This works because filter just builds the query without actually firing it to the database. Thus you can chain filters before an actual fetching command is called (like print
or .all()
).
Is it a good practice to do so or writing separate urls and views for this purpose is advised?
Depends on the use case, but filters using query params is a very popular way to add more customization while filtering.
Upvotes: 2
Reputation: 11363
If you want to pass in optional attributes to a view handler, you can use query params, which will be accessible by 'request.GETwhich is a
QueryDict` that can be accessed just like a dictionary.
Rewriting your example:
if request.method == 'GET':
books = Book.objects.filter(which_library = library_id)
genre = request.GET.get('genre', None)
status = request.GET.get('status', None)
if genre:
books = books.filter(genre = genre)
if status:
books = books.filter(status = status)
....
Note reassigning of book
on each filter. This is because a filter or any other queryset operation returns a new queryset and doesn't change the existing one.
Upvotes: 2