Continuing from the previous post. We found it is not a good way to do
Query or CreateonRESTFULapi because on one endpoint do more thing is ugly. But we really needFiltering.
CRUD need to filter result. So let's do it.
class BookViewSet(viewsets.ModelViewSet):
# queryset = Book.objects.all()
serializer_class = BookSerializer
permission_classes = [permissions.IsAuthenticatedOrReadOnly, IsOwnerOrReadOnly]
def perform_create(self, serializer):
serializer.save(owner=self.request.user)
def get_queryset(self):
"""
Optionally restricts the returned purchases to a given Book,
by filtering against a `id` query parameter in the URL.
"""
queryset = Book.objects.all()
id_value = self.request.query_params.get('id')
if id_value is not None:
queryset = queryset.filter(id=id_value)
return queryset
This code can let us to filter id and if user doesn't query then we give them all pagation result.
But user also want to filter Title fields or a lot of others. How to implement?
Thanks for django rest framework and django-filter. It have a powerful api. All we need to do is read the fucking document!Now delete our ugly code and find out why it work so awesome.
class BookViewSet(viewsets.ModelViewSet):
queryset = Book.objects.all()
serializer_class = BookSerializer
permission_classes = [permissions.IsAuthenticatedOrReadOnly, IsOwnerOrReadOnly]
+ filterset_fields = ['title', 'id']
def perform_create(self, serializer):
serializer.save(owner=self.request.user)
Here is the core code.lookup_expr maybe can let us implement like filter ?
How to implement like filter
Now we can use django-filter tom implement the same function what we had done before.Actually, the code above is an shortcut.
from django_filters import rest_framework as filters
class BookFilter(filters.FilterSet):
class Meta:
model = Book
fields = ['title', 'id']
class BookViewSet(viewsets.ModelViewSet):
queryset = Book.objects.all()
serializer_class = BookSerializer
permission_classes = [permissions.IsAuthenticatedOrReadOnly, IsOwnerOrReadOnly]
# filterset_fields = ['title', 'id', ]
filterset_class = BookFilter
def perform_create(self, serializer):
serializer.save(owner=self.request.user)
To support like filter we need to specify lookup_expr to contains
class BookFilter(filters.FilterSet):
title = filters.CharFilter(lookup_expr='contains')
id = filters.NumberFilter(lookup_expr='contains')
class Meta:
model = Book
fields = ['title', 'id']
PS: We can also choose other lookup_expr doc address.
And default value is:
Test Case
We can check how drf write test case.Here is my version.HHH
def test_filter_books(self):
b1 = Book.objects.create(title="Ender's Game", owner_id=self.defaultUser.id)
b2 = Book.objects.create(title="Rainbow Six", owner_id=self.defaultUser.id)
b3 = Book.objects.create(title="Snowcrash", owner_id=self.defaultUser.id)
filter = BookFilter(queryset=Book.objects.all())
self.assertQuerySetEqual(
filter.qs, [b1.pk, b2.pk, b3.pk], lambda o: o.pk, ordered=False
)
filter = BookFilter({"title": "Snowcrash"}, queryset=Book.objects.all())
self.assertQuerySetEqual(filter.qs, [b3.pk], lambda o: o.pk)
Thanks for reading!