DRF Querying and Pagination
Drf provides powerful tools for querying and paginating data in your APIs. These features allow you to filter, search, sort, and paginate your data efficiently, making it easier for clients to consume your API. Here’s an overview of how to use these features effectively:
Reference Code for Models and Serializers
# models.pyfrom django.db import models
class Collection(models.Model): title = models.CharField(max_length=255)
class Product(models.Model): title = models.CharField(max_length=255) price = models.DecimalField(max_digits=10, decimal_places=2) collection = models.ForeignKey('Collection', on_delete=models.CASCADE)# serializers.pyfrom rest_framework import serializersfrom .models import Product, Collection
class CollectionSerializer(serializers.ModelSerializer): class Meta: model = Collection fields = ['id', 'title']
class ProductSerializer(serializers.ModelSerializer): collection = CollectionSerializer()
class Meta: model = Product fields = ['id', 'title', 'price', 'collection']Filtering
Section titled “Filtering”Filtering allows clients to specify criteria to narrow down the results returned by the API. DRF provides built-in support for filtering using query parameters. You can use the django-filter package to easily add filtering capabilities to your views.
There are several types of filtering you can implement:
-
Query Parameter Filtering: Clients can filter results by passing query parameters in the URL.
-
django-filter: This package provides a powerful and flexible way to filter querysets based on model fields.
Query Parameter Filtering
Section titled “Query Parameter Filtering”You can implement basic filtering by overriding the get_queryset method in your view. For example:
# views.pyfrom rest_framework import viewsetsfrom .models import Productfrom .serializers import ProductSerializer
class ProductViewSet(viewsets.ModelViewSet): queryset = Product.objects.all() serializer_class = ProductSerializer
def get_queryset(self): queryset = super().get_queryset() collection_id = self.request.query_params.get('collection_id', None) if collection_id is not None: queryset = queryset.filter(collection_id=collection_id) return querysetThis allows clients to filter products by collection ID using a query parameter like ?collection_id=1. By implementing this, you can provide a more efficient and user-friendly API that allows clients to retrieve only the data they need.
Django-filter
Section titled “Django-filter”Django-filter is a powerful package that simplifies the process of adding filtering capabilities to your DRF views. It allows you to define filters based on your model fields and provides a clean API for clients to use. To use django-filter, you need to follow these steps:
-
To use
django-filter, you need to install it first:Terminal window uv add django-filter -
Add it to your
INSTALLED_APPSinsettings.py:INSTALLED_APPS = [# other apps'django_filters',]
# views.pyfrom django_filters.rest_framework import DjangoFilterBackendfrom rest_framework import viewsetsfrom .models import Productfrom .serializers import ProductSerializer
class ProductViewSet(viewsets.ModelViewSet): queryset = Product.objects.all() serializer_class = ProductSerializer filter_backends = [DjangoFilterBackend] filterset_fields = ['collection_id']Custom Filters
Section titled “Custom Filters”To enable more complex filtering, you can define a custom filter class using django-filter. This allows you to specify different types of filters for your model fields. For example:
# filters.pyfrom django_filters.rest_framework import FilterSetfrom .models import Product
class ProductFilter(FilterSet):
class Meta: model = Product fields = { 'price': ['gt', 'lt'], 'collection_id': ['exact'], }This custom filter allows clients to filter products based on price using exact matches, greater than, or less than comparisons. The collection_id field can still be filtered using exact matches. Here’s a breakdown of the available lookup types:
| Lookup | Meaning |
|---|---|
exact | = |
iexact | case-insensitive exact |
contains | LIKE %x% |
icontains | case-insensitive contains |
in | IN (...) |
gt / gte | > / >= |
lt / lte | < / <= |
range | BETWEEN |
isnull | IS NULL |
startswith | LIKE x% |
istartswith | case-insensitive startswith |
endswith | LIKE %x |
iendswith | case-insensitive endswith |
Then, you can use this custom filter in your view:
# views.pyfrom django_filters.rest_framework import DjangoFilterBackendfrom rest_framework import viewsetsfrom .models import Productfrom .serializers import ProductSerializerfrom .filters import ProductFilter
class ProductViewSet(viewsets.ModelViewSet): queryset = Product.objects.all() serializer_class = ProductSerializer filter_backends = [DjangoFilterBackend] filterset_class = ProductFilterSearching
Section titled “Searching”By using default query parameter we can implement searching functionality in our API. But it becomes more complex when we want to search across multiple fields or want to implement case-insensitive searching. DRF provides a built-in SearchFilter that allows you to easily add search functionality to your views. To use it, you need to add it to your view’s filter_backends and specify the fields you want to search on using the search_fields attribute. For example:
# views.pyfrom rest_framework import viewsetsfrom rest_framework.filters import SearchFilterfrom .models import Productfrom .serializers import ProductSerializer
class ProductViewSet(viewsets.ModelViewSet): queryset = Product.objects.all() serializer_class = ProductSerializer filter_backends = [SearchFilter] search_fields = [ 'title', 'description', 'collection__title', # you can also search on related # fields using double underscores ]By implementing search functionality, you can allow clients to easily find relevant data in your API by searching across multiple fields. The url for searching would look like this: ?search=keyword, where keyword is the term you want to search for. This makes it easier for clients to discover and access the data they need in your API.
Sorting
Section titled “Sorting”Sorting allows clients to specify the order in which results are returned by the API. DRF provides a built-in OrderingFilter that allows you to easily add sorting capabilities to your views. To use it, you need to add it to your view’s filter_backends and specify the fields you want to allow sorting on using the ordering_fields attribute. For example:
# views.pyfrom rest_framework import viewsetsfrom rest_framework.filters import OrderingFilterfrom .models import Productfrom .serializers import ProductSerializer
class ProductViewSet(viewsets.ModelViewSet): queryset = Product.objects.all() serializer_class = ProductSerializer filter_backends = [OrderingFilter] ordering_fields = ['title', 'price', 'last_update']By implementing sorting functionality, you can allow clients to specify the order of results based on different fields, making it easier for them to find the data they need in a way that suits their preferences. The url for sorting would look like this: ?ordering=field_name, where field_name is the name of the field you want to sort by. You can also specify descending order by prefixing the field name with a hyphen, like this: ?ordering=-field_name. This gives clients more control over how they access and view the data in your API.
Pagination
Section titled “Pagination”Pagination allows you to control the amount of data returned in a single API response, which can improve performance and reduce bandwidth usage. DRF provides several built-in pagination classes that you can use to paginate your API responses.
We can implement pagination by two ways:
-
Global Pagination: You can set a default pagination class in your
settings.pyfile, which will apply to all views that support pagination. -
View-Level Pagination: You can specify a pagination class for individual views by setting the
pagination_classattribute.
There are several types of pagination you can implement:
-
Page Number Pagination: This is the most common type of pagination, where clients can specify the page number and the number of items per page.
-
Limit-Offset Pagination: This type of pagination allows clients to specify the number of items to return (limit) and the starting point (offset) for the results.
Global Pagination
Section titled “Global Pagination”To set a default pagination class for your entire API, you can add the following configuration to your settings.py file:
# settings.pyREST_FRAMEWORK = { # other settings 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination', # you can also use 'rest_framework.pagination.LimitOffsetPagination' for limit-offset pagination 'PAGE_SIZE': 10,}View-Level Pagination
Section titled “View-Level Pagination”To specify a pagination class for an individual view, you can set the pagination_class attribute in your view. For example:
# pagination.pyfrom rest_framework.pagination import PageNumberPaginationfrom rest_framework.pagination import LimitOffsetPagination
class ProductPagination(PageNumberPagination): page_size = 10
class ProductLimitOffsetPagination(LimitOffsetPagination): default_limit = 10# views.pyfrom rest_framework import viewsetsfrom .models import Productfrom .serializers import ProductSerializerfrom .pagination import ProductPagination, ProductLimitOffsetPagination
class ProductViewSet(viewsets.ModelViewSet): queryset = Product.objects.all() serializer_class = ProductSerializer pagination_class = ProductPagination # or ProductLimitOffsetPaginationBy implementing pagination, you can improve the performance of your API and provide a better experience for clients by allowing them to retrieve data in manageable chunks. The url for page number pagination would look like this: ?page=2, where 2 is the page number you want to retrieve. For limit-offset pagination, the url would look like this: ?limit=10&offset=20, where limit specifies the number of items to return and offset specifies the starting point for the results. This allows clients to easily navigate through large datasets and access the data they need without overwhelming their applications or consuming excessive bandwidth.