drf - 基礎分頁組件 PageNumberPagination


數據准備

model.py文件

定義兩個表Car表和Brand表,其中Car中的brand字段外鍵關聯Brand表

from django.db import models
class BaseModel(models.Model):
    is_delete = models.BooleanField(default=False)
    create_time = models.DateTimeField(auto_now_add=True)
    class Meta:
        abstract = True


class Car(BaseModel):
    name = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=5, decimal_places=2)
    brand = models.ForeignKey('Brand', db_constraint=False, on_delete=models.DO_NOTHING, related_name='cars')
    @property
    def brand_name(self):
        return self.brand.name
    class Meta:
        db_table = 'old_boy_car'
        verbose_name = '汽車'
        verbose_name_plural = verbose_name
    def __str__(self):
        return self.name
    
class Brand(BaseModel):
    name = models.CharField(max_length=32)
    class Meta:
        db_table = 'old_boy_brand'
        verbose_name = '品牌'
        verbose_name_plural = verbose_name
    def __str__(self):
        return self.name

新建的serializer.py文件

brand字段只參與反序列化,brand_name只參與序列化

from rest_framework.serializers import ModelSerializer
from . import models
class CarModelSerializer(ModelSerializer):
    class Meta:
        model = models.Car
        fields = ('name','price','brand','brand_name')
        extra_kwargs = {
            "brand":{
                'write_only':True
            },
            'brand_name':{
                'read_only':True
            },
        }

分頁組件部分源碼分析一

通常是在獲取多條數據list方法中,進行分頁顯示。所以viewsets.py文件中list方法是分頁入口

class ListModelMixin:
    def list(self, request, *args, **kwargs):
        queryset = self.filter_queryset(self.get_queryset())  # 先篩選

        page = self.paginate_queryset(queryset)  # 后分頁,再序列化顯示
        if page is not None:
            serializer = self.get_serializer(page, many=True)
            return self.get_paginated_response(serializer.data)

        serializer = self.get_serializer(queryset, many=True)
        return Response(serializer.data)

1. 先調用paginate_queryset方法,此方法繼承至generics中的GenericAPIView類

def paginate_queryset(self, queryset):
    """
    Return a single page of results, or `None` if pagination is disabled.
    """
    if self.paginator is None:  # self.paginator分頁器,如果為None表示不分頁
        return None
    return self.paginator.paginate_queryset(queryset, self.request, view=self)

2. 調用paginator方法

@property
def paginator(self):
    """
    The paginator instance associated with the view, or `None`.
    """
    if not hasattr(self, '_paginator'):
        # 如果配置中的pagination_class為空就不分頁,所以要分頁就需要配置pagination_class類,else中實例化分頁類
        if self.pagination_class is None:
            self._paginator = None
        else:
            self._paginator = self.pagination_class()
    return self._paginator

3. 自定義pagination_class

4. 調用pagination_class類中的paginate_queryset方法

 

自定義分頁組件

新建的paginations.py文件,CarPageNumberPagination繼承了drf分頁組件中的PageNumberPagination類

from rest_framework.pagination import PageNumberPagination
class CarPageNumberPagination(PageNumberPagination):
    page_size = 3  # 設置每頁顯示的數量為3
    
    # 優先使用page_size_query_param的設置來顯示條數
    page_size_query_param = 'page_size'  # 自己輸入每頁顯示的條數
    max_page_size = 5  # 一頁顯示的最大條數,與page_size_query_param搭配使用
    
# url鏈接:car/?page=2  顯示第2頁,不寫默認顯示第一頁
# url鏈接:car/?page_size=4  每頁4條數據,若大於max_page_size,則顯示max_page_size設置的條數
# url鏈接:car/?page=2&page_size=4  每頁顯示4條,顯示第2頁
# url鏈接:car/?page=last  顯示最后一頁

注意:是根據數據顯示,若數據總條數只有5條,每頁顯示3條,這時第2頁就值顯示2條數據

view.py文件:需要自定義pagination_class

from rest_framework.viewsets import ModelViewSet
from . import models, serializer
from .paginations import CarPageNumberPagination

class CarModelViewSet(ModelViewSet):
    queryset = models.Car.objects.filter(is_delete=False)
    serializer_class = serializer.CarModelSerializer
    # 自定義pagination_class
    parser_classes = CarPageNumberPagination

 

分頁組件部分源碼分析二

1. 調用CarPageNumberPagination中的paginate_queryset方法

def paginate_queryset(self, queryset, request, view=None):
    """
    Paginate a queryset if required, either returning a
    page object, or `None` if pagination is not configured for this view.
    """
    page_size = self.get_page_size(request)  # 獲取page_size
    if not page_size:
        return None

    paginator = self.django_paginator_class(queryset, page_size)  # 分頁器,django.core.paginator中的Paginator分頁器類
    page_number = request.query_params.get(self.page_query_param, 1)  # page_query_param為'page',在url接口中page=2表示第2頁,page就是鏈接的頁數表示。默認為page=1
    if page_number in self.last_page_strings:  
        # last_page_strings為('last',),如果url輸入page=last,表示最后一頁
        page_number = paginator.num_pages

    try:
        self.page = paginator.page(page_number)
    except InvalidPage as exc:
        msg = self.invalid_page_message.format(
            page_number=page_number, message=str(exc)
        )
        raise NotFound(msg)

    if paginator.num_pages > 1 and self.template is not None:
        # The browsable API should display pagination controls.
        self.display_page_controls = True

    self.request = request
    return list(self.page)

2. 調用get_page_size方法,獲取page_size:每頁顯示的數量

def get_page_size(self, request):
    if self.page_size_query_param:  # drf中默認設置為None,可以在CarPageNumberPagination類中自定義,如果沒有設置就直接返回page_size的值
        try:
            return _positive_int(
                request.query_params[self.page_size_query_param],  
                strict=True,
                cutoff=self.max_page_size  # 一頁顯示的最大數量
            )
        except (KeyError, ValueError):
            pass

    return self.page_size  # drf中默認設置為None,可以在類中配置

3. 設置了page_size_query_param時,獲取的page_size每頁顯示的數量。調用_positive_int方法

def _positive_int(integer_string, strict=False, cutoff=None):
    """
    Cast a string to a strictly positive integer.
    """
    ret = int(integer_string) # url傳入的值,如page_size_query_param='page_size',在url中:car/?page_size=4.則ret=4
    if ret < 0 or (ret == 0 and strict):
        raise ValueError()
    if cutoff:
        return min(ret, cutoff)
    return ret  # 此時ret為4就是返回的page_size


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM