數據准備
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