Understanding django drf's Filtering

64 阅读1分钟

Continuing from the previous post. We found it is not a good way to do Query or Create on RESTFUL api because on one endpoint do more thing is ugly. But we really need Filtering.

image.png

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)

image.png

image.png

image.png

image.png

image.png

image.png

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.

image.png

And default value is:

image.png


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!