Django rest_framework 源码学习笔记(二)之分页器

发布时间:2020-12-16 浏览量: 170 文章分类: python

关于Django rest_framework源码学习笔记,我会从源码逐步入手,这个是一个系列,第一篇的文章可以查看Django rest_framework 源码学习笔记(一)之序列化器 这篇主要是关于rest_framework.pagination分页相关。

分页相关的类DRF主要提供了三种方法,他们都继承与BasePagination类 如果要设置全局分页可以在settings中增加REST_FRAMEWORK里面增加PAGE_SIZE页码数。其余的参数我在下面在逐个说明

  • 分页,传递页码,看n页,每页显示n条数据PageNumberPagination
  • 基于limit分页,在n个位置,向后查看n条数据LimitOffsetPagination
  • 基于加密分页,只能看上一页和下一页,基于位置来查询,数据量大的话速度可以得到保证CursorPagination

基于PageNumberPagination实现分页

这是一个常规分页功能实现,其实已经满足了日常的分页需求。

from rest_framework.response import Response
from rest_framework.pagination import PageNumberPagination
from rest_framework.views import APIView

# 分页相关
class MyPageNumberPagination(PageNumberPagination):
    page_size = 3
    page_query_param = 'page'
    page_size_query_param = 'size' #每页几个数据
    max_page_size = None # size 的最大值是多少

# 类视图函数
class ArticleView(APIView):
    def get(self,request,*args,**kwargs):
        article_list_obj = article_models.Article.objects.all() # 获取所有文章
        pg = MyPageNumberPagination()
        pager_article = pg.paginate_queryset(queryset=article_list_obj,request=request,view=self) # 分页
        ser = serializers.ArticleSerializer(instance=pager_article,many=True) # 直接序列化分页结果即可
        return Response(ser.data) 

页面返回结果如下

[
    {
        "id": 1,
        "title": "前端多列布局备忘",
        "content": "# 自适应布局解决方案\r\n##  一列定宽、一列自适应(定宽+自适应)\r\n+ 使用`float + margin` 方法解决\r\n假设左边我们定宽100px,右边不定宽,那么实现可以如下\r\n\r\n```html\r\n        .left{\r\n            float: left;\r\n            width: 100px;\r\n        }\r\n        .right {\r\n            margin-left: 100px;\r\n        }\r\n\t\t\r\n    <div class=\"parent\">\r\n        <div class=\"left\">\r\n            <p>left</p>\r\n        </div>\r\n        <div class=\"right\">\r\n            <p>right</p>\r\n            <p>right</p>\r\n        </div>\r\n    </div>\r\n```\r\n\r\n+ 使用`float + overflow` 方法解决\r\n同样的问题,在换一个解决方法试试\r\n\r\n```html\r\n        .left{\r\n            float: left;\r\n            width: 100px;\r\n        }\r\n        .right {\r\n            overflow: hidden;\r\n        }\r\n    <div class=\"parent\">\r\n        <div class=\"left\">\r\n            <p>left</p>\r\n        </div>\r\n        <div class=\"right\">\r\n            <p>right</p>\r\n            <p>right</p>\r\n        </div>\r\n    </div>\r\n\r\n```\r\n\r\n+ 使用`table`方法解决\r\n\r\n```html\r\n        .parent{\r\n            display: table;\r\n            width: 100%;\r\n            table-layout: fixed;\r\n        }\r\n        .left,.right{\r\n            display: table-cell;\r\n        }\r\n        .left{\r\n            width: 100px;\r\n        }\r\n\t\t\r\n    <div class=\"parent\">\r\n        <div class=\"left\">\r\n            <p>left</p>\r\n        </div>\r\n        <div class=\"right\">\r\n            <p>right</p>\r\n            <p>right</p>\r\n        </div>\r\n    </div>\r\n```\r\n\r\n+ 使用`flex`方法解决\r\n\r\n```html\r\n        .parent{\r\n            display: flex;\r\n        }\r\n        .left{\r\n            width: 100px;\r\n        }\r\n        .right{\r\n            flex: 1;\r\n        }\r\n\t\t\r\n```\r\n\r\n##  两页定宽+自适应\r\n\r\n```html\r\n        .left,.center{\r\n            float: left;\r\n            width: 100px;\r\n        }\r\n        .right{\r\n            overflow: hidden;\r\n        }\r\n\t\t\r\n    <div class=\"parent\">\r\n        <div class=\"left\">\r\n            <p>left</p>\r\n        </div>\r\n        <div class=\"center\">\r\n            <p>center</p>\r\n        </div>\r\n        <div class=\"right\">\r\n            <p>right</p>\r\n            <p>right</p>\r\n        </div>\r\n    </div>\r\n```\r\n其余的方法等同上面的类似\r\n\r\n## 不定宽 + 自适应解决方案\r\n+ 使用`float + overflow`方法解决\r\n\r\n```html\r\n        .left{\r\n            float: left;\r\n        }\r\n        .right{\r\n            overflow: hidden;\r\n        }\r\n\t\t\r\n    <div class=\"parent\">\r\n        <div class=\"left\">\r\n            <p>left</p>\r\n        </div>\r\n        <div class=\"right\">\r\n            <p>right</p>\r\n            <p>right</p>\r\n        </div>\r\n    </div>\r\n```\r\n\r\n+ 使用`table`方法解决\r\n\r\n```html\r\n        .parent{\r\n            display: table;\r\n            width: 100%;\r\n        }\r\n        .left,.right{\r\n            display: table-cell;\r\n        }\r\n\r\n    <div class=\"parent\">\r\n        <div class=\"left\">\r\n            <p>left</p>\r\n        </div>\r\n        <div class=\"right\">\r\n            <p>right</p>\r\n            <p>right</p>\r\n        </div>\r\n    </div>\r\n```\r\n\r\n+ 使用`flex`方法实现\r\n\r\n```html\r\n        .parent{\r\n            display: flex;\r\n        }\r\n        .left{\r\n            width: 100%;\r\n        }\r\n        .right{\r\n            flex: 1;\r\n        }\r\n\r\n    <div class=\"parent\">\r\n        <div class=\"left\">\r\n            <p>left</p>\r\n        </div>\r\n        <div class=\"right\">\r\n            <p>right</p>\r\n            <p>right</p>\r\n        </div>\r\n    </div>\r\n\r\n```\r\n**多列不定宽+ 自适应解决方法上面的方法也同样适用**\r\n\r\n# 等宽布局解决方案\r\n\r\n## 多列等宽布局\r\n\r\n+ 使用`float`方法解决\r\n这个没什么好说的,比如我们一行有4个,直接`float-left:25%`即可。但是问题如果我们想增加间距等问题就会比较麻烦。\r\n\r\n首先我知道**整列的宽度是100% = C,每列的宽度=W,每列之间的间隔=G**,因为4列之间只有3个间隔,所以公式可以如下\r\n`C = W * N + G * (N - 1)`也可以在换算一下,也就是`C = (W +G) *N -G`,在换算也就是`C + G = (W + G) * N`,也就是给外面的父容器+上间隔的像素。\r\n\r\n```html\r\n        .parent{\r\n            margin-left: -20px;\r\n        }\r\n        .column{\r\n            float: left;\r\n            width: 25%;\r\n            padding-left: 20px;\r\n            box-sizing: border-box;\r\n        }\r\n\r\n    <div class=\"parent\">\r\n        <div class=\"column\">1</div>\r\n        <div class=\"column\">2</div>\r\n        <div class=\"column\">3</div>\r\n        <div class=\"column\">4</div>\r\n    </div>\r\n\r\n```\r\n\r\n+ 使用`table`实现\r\n\r\n```html\r\n        .parent-fix{\r\n            margin-left: -20px;\r\n        }\r\n        .parent{\r\n            display: table;\r\n            width: 100%;\r\n            table-layout: fixed;\r\n        }\r\n        .column{\r\n            display: table-cell;\r\n        }\r\n\r\n    <div class=\"parent-fix\">\r\n        <div class=\"parent\">\r\n            <div class=\"column\">1</div>\r\n            <div class=\"column\">2</div>\r\n            <div class=\"column\">3</div>\r\n            <div class=\"column\">4</div>\r\n        </div>\r\n    </div>\r\n```\r\n\r\n+ 使用`flex`实现\r\n\r\n```html\r\n        .parent{\r\n            display: flex;\r\n        }\r\n        .column{\r\n            flex: 1;\r\n        }\r\n\r\n    <div class=\"parent\">\r\n        <div class=\"column\">1</div>\r\n        <div class=\"column\">2</div>\r\n        <div class=\"column\">3</div>\r\n        <div class=\"column\">4</div>\r\n    </div>\r\n\r\n```\r\n\r\n# 等高布局解决方案\r\n\r\n## 多列的时候左右两列一样高\r\n\r\n等高布局的意思就是其中有一列的内容被撑开的时候,另外一列一样要被撑开\r\n\r\n+ 使用`table`解决\r\n\r\n```html\r\n        .parent{\r\n            display: table;\r\n            width: 100%;\r\n            table-layout: fixed;\r\n        }\r\n        .left,.right{\r\n            display: table-cell;\r\n        }\r\n        .left{\r\n            width: 100px;\r\n\t\t\tborder-right: 20px solid transparent;\r\n            background-clip: padding-box;\r\n        }\r\n\r\n    <div class=\"parent\">\r\n        <div class=\"left\">\r\n            <p>left</p>\r\n        </div>\r\n        <div class=\"right\">\r\n            <p>right</p>\r\n            <p>right</p>\r\n        </div>\r\n    </div>\r\n```\r\n\r\n+ 使用`flex`解决\r\n\r\n```html\r\n        .parent{\r\n            display: flex;\r\n        }\r\n        .left{\r\n            width: 100px;\r\n            margin-right: 20px;\r\n        }\r\n        .right{\r\n            flex: 1;\r\n        }\r\n\r\n    <div class=\"parent\">\r\n        <div class=\"left\">\r\n            <p>left</p>\r\n        </div>\r\n        <div class=\"right\">\r\n            <p>right</p>\r\n            <p>right</p>\r\n        </div>\r\n    </div>\r\n```\r\n\r\n+ 使用`float`解决(伪等高)\r\n\r\n```html\r\n        .left{\r\n            float: left;\r\n            width: 100px;\r\n        }\r\n        .right{\r\n            overflow: hidden;\r\n        }\r\n        .left,.right{\r\n            padding-bottom: 9999px;\r\n            margin-bottom: -9999px;\r\n        }\r\n        .parent{\r\n            overflow: hidden;\r\n        }\r\n\r\n    <div class=\"parent\">\r\n        <div class=\"left\">\r\n            <p>left</p>\r\n        </div>\r\n        <div class=\"right\">\r\n            <p>right</p>\r\n            <p>right</p>\r\n        </div>\r\n    </div>\r\n```\r\n\r\n一句话总结就是`flex`天下无敌,简单方便。",
        "star": 0,
        "views": 162,
        "create_time": "2020-12-14T21:36:29.989178",
        "is_true": true,
        "recommend": false,
        "article_class": {
            "id": 2,
            "name": "前端相关",
            "key": "web",
            "cover_img": "https://www.wuyabala.com/media/media/fd31db8484e84dd589f9a6028f2878fd.jpeg"
        }
    },
    {
        "id": 2,
        "title": "前端页面居中布局常见解决方案备忘",
        "content": "学习前端也有好一段时间了,刚好做一个备忘,关于常见的水平居中、垂直居中、居中布局的解决方案备忘。\r\n\r\n# 水平居中解决方法\r\n水平居中的效果很简单,就是外面有一个容器,里面也有一个容器,想让里面的容器水平居中在外面的容器中的解决办法。\r\n\r\n+ 使用`inline-block + text-align` 解决\r\n\r\n```html\r\n\r\n \t  .child{\r\n            display: inline-block;\r\n        }\r\n        .parent{\r\n            text-align: center;\r\n        }\r\n\r\n     <div class=\"parent\">\r\n        <div class=\"child\">DEMO</div>\r\n    </div>\r\n```\r\n\r\n+ 使用`table+margin`解决\r\n\r\n```html\r\n\r\n        .child{\r\n            display: table;\r\n            margin: 0 auto;\r\n        }\r\n\r\n    <div class=\"parent\">\r\n        <div class=\"child\">DEMO</div>\r\n    </div>\r\n```\r\n\r\n+ 使用`absolute + transform`解决\r\n\r\n```html\r\n        .child{\r\n           position: relative;\r\n        }\r\n        .parent{\r\n            position: absolute;\r\n            left: 50%;\r\n            transform: translateX(-50%);\r\n        }\r\n\r\n    <div class=\"parent\">\r\n        <div class=\"child\">DEMO</div>\r\n    </div>\r\n```\r\n\r\n+ 使用`flex + justify-content`解决\r\n\r\n```html\r\n     .child{\r\n           display: flex;\r\n           justify-content: center;\r\n    }\r\n\t\t\r\n    <div class=\"parent\">\r\n        <div class=\"child\">DEMO</div>\r\n    </div>\r\n```\r\n\r\n除了 `justify-content`方法以外,也可以使用这样的方法\r\n\r\n```html\r\n        .child{\r\n            margin: 0 auto;\r\n        }\r\n        .parent{\r\n            display: flex;\r\n        }\r\n    <div class=\"parent\">\r\n        <div class=\"child\">DEMO</div>\r\n    </div>\r\n```\r\n\r\n# 垂直居中解决方案\r\n\r\n+ 使用`table-cell + vertical-align`方法解决\r\n\r\n```html\r\n        .parent{\r\n            display: table-cell;\r\n            vertical-align: middle;\r\n        }\r\n    <div class=\"parent\">\r\n        <div class=\"child\">DEMO</div>\r\n    </div>\r\n```\r\n\r\n+ 使用`absolute + transform`方法解决\r\n\r\n```html\r\n        .parent{\r\n            position: relative;\r\n        }\r\n        .child{\r\n            position: absolute;\r\n            top: 50%;\r\n\t\t\ttransform: translateY(-50%);\r\n        }\r\n\t\t\r\n\t<div class=\"parent\">\r\n        <div class=\"child\">DEMO</div>\r\n    </div>\r\n\t\t\r\n```\r\n\r\n+ 使用`flex + align-item` 方法解决\r\n\r\n```html\r\n        .parent{\r\n            display: flex;\r\n            align-items: center;\r\n        }\r\n\t\t\r\n\t<div class=\"parent\">\r\n        <div class=\"child\">DEMO</div>\r\n    </div>\r\n\t\r\n```\r\n\r\n# 居中解决方案(水平居中+垂直居中)\r\n\r\n+ `inline-block + text-align + table-cell + vertical-align`解决\r\n\r\n```html\r\n        .parent{\r\n            text-align: center;\r\n            display: table-cell;\r\n            vertical-align: middle;\r\n        }\r\n        .child{\r\n            display: inline-block;\r\n        }\r\n\t\t\r\n\t<div class=\"parent\">\r\n        <div class=\"child\">DEMO</div>\r\n    </div>\r\n```\r\n\r\n+ 使用`absolute + transform` 方法解决\r\n\r\n```html\r\n        .parent{\r\n            position: relative;\r\n        }\r\n        .child{\r\n            position: absolute;\r\n            left: 50%;\r\n            top: 50%;\r\n            transform: translate(-50%,-50%);\r\n        }\r\n\r\n\t<div class=\"parent\">\r\n        <div class=\"child\">DEMO</div>\r\n    </div>\r\n```\r\n\r\n+ 使用`flex + justify-content + align-items` 方法解决\r\n\r\n```html\r\n        .parent{\r\n            display: flex;\r\n            justify-content: center;\r\n            align-items: center;\r\n        }\r\n\r\n    <div class=\"parent\">\r\n        <div class=\"child\">DEMO</div>\r\n    </div>\r\n\t\t\r\n```\r\n\r\n通过上面的几种方法其实都能实现各种居中的方法,但是我个人建议就是能用`flex`就用`flex`解决。真的非常方便。",
        "star": 0,
        "views": 150,
        "create_time": "2020-12-14T21:36:58.217849",
        "is_true": true,
        "recommend": false,
        "article_class": {
            "id": 2,
            "name": "前端相关",
            "key": "web",
            "cover_img": "https://www.wuyabala.com/media/media/fd31db8484e84dd589f9a6028f2878fd.jpeg"
        }
    },
    {
        "id": 3,
        "title": "Django rest_framework  源码学习笔记(一)之序列化器",
        "content": "Django rest_framework 也简称叫做`DRF`,虽然有使用,但是用的并不多,顺带这次有时间,就抽空也看了一下源码。果然自己的认知加深了很多,而且之前确实我自己用的很潜,所以希望可以将自己学习的收获记录一下。\r\n\r\n> #前言\r\n整体来说`DRF`是一个不错的框架,也遵循了`restful`风格,作者也贴心的帮助实现了很多的功能,让快速出活变的更简单。所以还是值得深入学习一下,同时理解一下作者的思路。\r\n\r\n因为这个框架涉及的内容还是很庞大,所以可能会拆分开 一点一点来进行总结。\r\n\r\n根据官网的学习方式,我们也从序列化器开始从代码看进去。`serializers`\r\n\r\n**能看到这篇文章的同学,我假设你已经会安装使用django,同时也对`python`、`django`有一定的了解**\r\n**不会`DRF`也没有问题的,认真看,保证你能看得懂**\r\n\r\n# 序列化器\r\n##  导入\r\n``from rest_framework import serializers`\r\n\r\n官方一共提供了以下6个方法,让我们使用。\r\n```python\r\nBaseSerializer(基类)\r\nSerializerMetaclass(不常用)\r\nSerializer(常用)\r\nListSerializer(不常用)\r\nModelSerializer(常用)\r\nHyperlinkedModelSerializer (不常用)\r\n```\r\n对了,这里先同步一下 数据的格式和样式,为了方便阅读起见,我尽量都用最简模式创建。\r\n```python\r\nfrom django.db import models\r\n\r\nclass ArticleType(models.Model):\r\n    type_name = models.CharField(max_length=32)\r\n\r\nclass Author(models.Model):\r\n    name = models.CharField(max_length=64)\r\n\r\nclass ArticleInfo(models.Model):\r\n    title = models.CharField(max_length=100, verbose_name='标题')\r\n    content = models.TextField(verbose_name='正文')\r\n    article_class = models.ForeignKey('ArticleType',on_delete= models.DO_NOTHING, verbose_name='文章分类')\r\n    auth = models.ForeignKey('Author',on_delete=models.CASCADE,verbose_name='作者')\r\n    views = models.IntegerField(default= 0, verbose_name='浏览量')\r\n    create_time = models.DateTimeField(auto_now_add=True, verbose_name='发布时间')\r\n    is_true = models.BooleanField(default=True, verbose_name='是否显示')\r\n\t\r\n```\r\n接下来就正常的同步数据,创建表,然后就开始我们这次的正题了。\r\n\r\n## 基于`Serializer`类的实现\r\n这是一个基础类,可以快速理解序列化器,我们先来一个最简单的,通过url实现获取文章的分类。(别忘记先去增加一下假数据)不用多,我的数据样式如下\r\n![Django rest_framework  源码学习笔记](/media/editor/01_20201214173836202913.jpg)\r\n接下来我们先按照传统的模式实现一下。\r\n```python\r\nfrom rest_framework import serializers # 导入序列化器\r\nfrom django.views import View # 导入django视图\r\nfrom web_api.models import artile_models # 引入自己实现的model类\r\nfrom django.http import JsonResponse # 引入django的JsonResponse\r\n\r\nclass ArticleTypeView(APIView):\r\n    def get(self,request,*args,**kwargs):\r\n        # 定义存放返回的json数据列表\r\n        ret = {'article_type':[]}\r\n        # 查询数据\r\n        article_type_obj = xuexi01_models.ArticleType.objects.all()\r\n        # 遍历装载\r\n        for item in article_type_obj:\r\n            ret['article_type'].append(\r\n                {\r\n                    'id':item.id,\r\n                    'title':item.type_name\r\n                }\r\n            )\r\n        return JsonResponse(ret)\r\n```\r\n接下来,在用`DRF`的`Serializer`类改写一下\r\n\r\n```python\r\nfrom django.views import View\r\nfrom web_api.models import artile_models\r\nfrom django.http import JsonResponse\r\nfrom rest_framework import serializers\r\n\r\n\r\nclass ArticleTypeSerializers(serializers.Serializer):\r\n    # 继承 rest_framework 的serializers中的Serializer类\r\n    id = serializers.IntegerField()\r\n    type_name = serializers.CharField()\r\n\r\nclass ArticleTypeView(View):\r\n    def get(self,request,*args,**kwargs):\r\n        # 定义存放返回的json数据列表\r\n        ret = {'article_type':[]}\r\n        # 查询数据\r\n        article_type_obj = xuexi01_models.ArticleType.objects.all()\r\n        # 序列化\r\n        ser = ArticleTypeSerializers(instance=article_type_obj,many=True)\r\n        ret['article_type'] = ser.data\r\n        return JsonResponse(ret)\r\n\t\t\r\n```\r\n如果仔细对比,会发现`ArticleTypeView`里面只是将以前的循环填充数组,变成了引用一个外部类,然后传给他参数,在将结果赋值就完成了。\r\n\r\n一步一步来,先继续研究`ArticleTypeSerializers`的实现,`id = serializers.IntegerField()、type_name = serializers.CharField()`有没有发现他很像我们在`models`定义的数据格式,这里其实就是你需要什么数据,就可以列在这里,序列化器就会根据你列的数据进行序列化。\r\n不用担心格式不知道的问题,在`serializers.py`的实现中,作者导入了他支持的模式,所以我们能看到支持的字段格式如下,基本和`Django`的字段一致。只是将`models.CharField()`变成了`serializers.CharField()`而已。\r\n\r\n```python\r\n# 附所有支持的字段格式\r\nfrom rest_framework.fields import (  # NOQA # isort:skip\r\n    BooleanField, CharField, ChoiceField, DateField, DateTimeField, DecimalField,\r\n    DictField, DurationField, EmailField, Field, FileField, FilePathField, FloatField,\r\n    HiddenField, HStoreField, IPAddressField, ImageField, IntegerField, JSONField,\r\n    ListField, ModelField, MultipleChoiceField, NullBooleanField, ReadOnlyField,\r\n    RegexField, SerializerMethodField, SlugField, TimeField, URLField, UUIDField,\r\n)\r\n```\r\n\r\n在接下来继续看我们在实例化了类以后,传递的`instance`、`many`两个参数是干嘛的。\r\n由于`Serializer`继承了`BaseSerializer`.所以需要的话,可以直接从源码点进去具体看作者的实现。\r\n暂时只要先知道`instance`可以将数据库查询后的`QuerySet`结果传给他、`many`主要是告诉他 结果是多条还是单条,如果是多条就是`True`,单条就是`False`或者不传。\r\n\r\n那么 现在又出现了一个新的问题,因为返回的字段都是我自己在数据库定义好的,假设,我想将这里的`id`在返回给前端的时候变成`aid`,那么,我们还可以在序列化器的时候增加`source`字段。比如\r\n\r\n```python\r\n    aid = serializers.IntegerField(source='id')\r\n    name = serializers.CharField(source='type_name')\r\n```\r\n也就是在`source`里面填写原始的字段名,然后变量写我们想返回前端的字段\r\n那么返回的结果也会从之前的\r\n\r\n```json\r\n{\"article_type\":[{\"id\":1,\"type_name\":\"前端开发\"},{\"id\":2,\"type_name\":\"后端开发\"},{\"id\":3,\"type_name\":\"运维心得\"},{\"id\":4,\"type_name\":\"心情杂谈\"}]}\r\n```\r\n变成\r\n```json\r\n{\"article_type\":[{\"aid\":1,\"name\":\"前端开发\"},{\"aid\":2,\"name\":\"后端开发\"},{\"aid\":3,\"name\":\"运维心得\"},{\"aid\":4,\"name\":\"心情杂谈\"}]}\r\n```\r\n这样也就实现了我们自定义的变量名转换。\r\n\r\n## 基于`ModelSerializer` 的实现\r\n还是同样的数据,我们在切换一下序列化器的代码。\r\n```python\r\nclass ArticleTypeSerializers(serializers.ModelSerializer):\r\n    # 继承 rest_framework 的serializers中的Serializer类\r\n    class Meta:\r\n        model = artile_models.ArticleType\r\n        fields = '__all__'\r\n```\r\n其余代码不变,主要我们主要是将`ArticleTypeSerializers`类的继承从`Serializer`变成了`ModelSerializer`。根据官方的介绍,在常规的基础上,增加了`.create()`和`.update()`方法的实现,我们只要将数据库进行绑定,则自动填充一组数据。\r\n也可以通过`fields`来指定我们需要的字段.\r\n如果我们将`fields = '__all__'`变成`fields = ['type_name']`那么返回的内容就没有`id`这个字段了。\r\n同时值得注意的是,因为`ModelSerializer`类继承的是`Serializer`类。所以他们其实也是可以混合使用的。\r\n接下来,还是老问题,我想将`id`变成`sid`,则可以这么写。所以可以看到,直观的结论就是`ModelSerializer`可以节省我们更多的代码\r\n\r\n```python\r\nclass ArticleTypeSerializers(serializers.ModelSerializer):\r\n    sid = serializers.CharField(source='id')\r\n    class Meta:\r\n        model = artile_models.ArticleType\r\n        fields = ['type_name','sid']\r\n```\r\n至于官方说的 `.create()`和`.update()`方法,我后面在统一总结。这里先跳过。\r\n\r\n至于那些不常用的,就暂时先不放这里说了。以后用到在慢慢补充。\r\n## 补充方法说明\r\n刚才我们一直都是在拿单表的数据,也就是没有涉及到`ForeignKey`或`ManyToManyField`的情况。\r\n现在我们在随便给`ArticleInfo`和`Author`表都增加一点数据。\r\n然后在使用同样的方式完成\r\n```python\r\nclass ArticleInfoSerializers(serializers.ModelSerializer):\r\n    class Meta:\r\n        model = artile_models.ArticleInfo\r\n        fields = '__all__'\r\n\r\nclass ArticleInfoView(View):\r\n    def get(self,request,*args,**kwargs):\r\n        ret = {'article_list': []}\r\n        article_info_obj = artile_models.ArticleInfo.objects.all()\r\n        ser = ArticleInfoSerializers(instance=article_info_obj,many=True)\r\n        ret['article_list'] = ser.data\r\n        return JsonResponse(ret)\r\n```\r\n页面返回结果就出现了,我们创建的2篇文章数据\r\n```json\r\n{\"article_list\":[{\"id\":1,\"title\":\"第一篇文章\",\"content\":\"这是第一篇文章的正文\",\"views\":20,\"create_time\":null,\"is_true\":true,\"article_class\":1,\"auth\":1},{\"id\":2,\"title\":\"第二篇文章\",\"content\":\"这是第二篇文章的正文\",\"views\":35,\"create_time\":null,\"is_true\":true,\"article_class\":1,\"auth\":1}]}\r\n```\r\n首先说说 这里存在的问题`article_class`和`auth`返回的都是关联表的数字id1,而不是对应的中文。\r\n要解决这个问题,第一种方式是使用`.`解决,也就是下面这样\r\n```python\r\nclass ArticleInfoSerializers(serializers.ModelSerializer):\r\n    auth = serializers.CharField(source=\"auth.name\")\r\n    class Meta:\r\n        model = xuexi01_models.ArticleInfo\r\n        fields = '__all__'\r\n```\r\n因为我们在数据库取回来的`auth`也是一个对象,所以可以通过`.`的方法继续往下取值。\r\n所以也就是上面那样。这样可以解决。\r\n```json\r\n{\"article_list\":[{\"id\":1,\"auth\":\"endpein\",\"title\":\"第一篇文章\",\"content\":\"这是第一篇文章的正文\",\"views\":20,\"create_time\":null,\"is_true\":true,\"article_class\":1},{\"id\":2,\"auth\":\"endpein\",\"title\":\"第二篇文章\",\"content\":\"这是第二篇文章的正文\",\"views\":35,\"create_time\":null,\"is_true\":true,\"article_class\":1},{\"id\":3,\"auth\":\"endpein\",\"title\":\"这是第三篇文章\",\"content\":\"这是第三篇文章的正文\",\"views\":15,\"create_time\":\"2020-12-14T20:40:07.253158\",\"is_true\":true,\"article_class\":2}]}\r\n```\r\n可以看到,作者`auth`已经变成了`endpein`。\r\n\r\n第二种方法就是使用`DRF`里面自带的`depth`深度参数\r\n```python\r\nclass ArticleInfoSerializers(serializers.ModelSerializer):\r\n    class Meta:\r\n        model = artile_models.ArticleInfo\r\n        fields = '__all__'\r\n        depth = 1\r\n```\r\n而这种返回的格式在和上面的有一些区别,类似下面这样\r\n```json\r\n{\"article_list\":[{\"id\":1,\"title\":\"第一篇文章\",\"content\":\"这是第一篇文章的正文\",\"views\":20,\"create_time\":null,\"is_true\":true,\"article_class\":{\"id\":1,\"type_name\":\"前端开发\"},\"auth\":{\"id\":1,\"name\":\"endpein\"}},{\"id\":2,\"title\":\"第二篇文章\",\"content\":\"这是第二篇文章的正文\",\"views\":35,\"create_time\":null,\"is_true\":true,\"article_class\":{\"id\":1,\"type_name\":\"前端开发\"},\"auth\":{\"id\":1,\"name\":\"endpein\"}},{\"id\":3,\"title\":\"这是第三篇文章\",\"content\":\"这是第三篇文章的正文\",\"views\":15,\"create_time\":\"2020-12-14T20:40:07.253158\",\"is_true\":true,\"article_class\":{\"id\":2,\"type_name\":\"后端开发\"},\"auth\":{\"id\":1,\"name\":\"endpein\"}}]}\r\n```\r\n可以看到`auth`变成了一个字典,返回包含了`key`和`value`。\r\n所以其实`depth`参数 适用于`ForeignKey`和`ManyToManyField`两种方法。\r\n具体如何使用就需要看个人的选择来决定。\r\n\r\n假如我们使用了`depth`参数,但是对返回的结果又不是很满意,希望可以有更细粒度的控制。那么我们也可以使用`SerializerMethodField`自定义序列化器\r\n例如:\r\n```python\r\nclass ArticleInfoSerializers(serializers.ModelSerializer):\r\n    article_class = serializers.SerializerMethodField()\r\n    class Meta:\r\n        model = artile_models.ArticleInfo\r\n        fields = '__all__'\r\n        depth = 1\r\n    def get_article_class(self,row):\r\n        return row.article_class.type_name\r\n```\r\n这样第一种方式和第二种方式则返回的样式就一致了。(`row`就是每行返回的对象,我们可以用`.`的方法继续往下找)\r\n`SerializerMethodField` 自定义序列化器,在使用的同时,需要在下面增加对应的get_xxx 函数来完成具体的数据实现\r\n\r\n## 补充说明\r\n日常的数据库中,除了常用`字段`、`ForeignKey`、`ManyToManyField`外,有时候还会使用`choices`来指定选项。\r\n这种的只要在`source='get_字段名_display'`即可",
        "star": 0,
        "views": 12,
        "create_time": "2020-12-14T21:37:17.518055",
        "is_true": true,
        "recommend": false,
        "article_class": {
            "id": 1,
            "name": "python",
            "key": "python",
            "cover_img": "https://www.wuyabala.com/media/media/5e438e833b034e2ca1b8cd4501e78476.jpg"
        }
    }
]

可以看到,很完美,但是实际我们存储的内容不止3条,那如何翻页呢?还记得上面我们在分页类配置的参数吗page_query_param='page',那么下一页只要用http://127.0.0.1:8888/api/v1/article/?page=2访问即可。

基于LimitOffsetPagination实现分页

他也是继承BasePagination,但是通过查看源码,配置不太一样了。

    default_limit = api_settings.PAGE_SIZE # 设置每页显示几个
    limit_query_param = 'limit' # 每页显示几条内容
    offset_query_param = 'offset' # 页码
    max_limit = None  # 每页显示条数的最大值

这样一看,其实还是很简单,接下来,我们在改造上面的类,从新来在配置一下

class MyLimitOffsetPagination(LimitOffsetPagination):
    default_limit = 2
    limit_query_param = 'limit'
    max_limit = 3
    offset_query_param = 'offset'

视图类不变,我们继续请求,并在请求的参数中增加?offset=2?limit=3?offset=2&limit=3你就明白了。

基于CursorPagination实现分页

这个功能有一个优点,结合我们后面要说的节流,可以很有效的起到限制爬虫的功能,因为他可以对页码进行加密处理。 先看看需要我们修改的配置如下

class MyCursorPagination(CursorPagination):
    cursor_query_param = 'cursor' # 页码标识
    page_size = 2 # 每页显示的条数
    ordering = '-created' # 排序规则,需要修改

按照我们的文章规则来修改一下

class MyPageNumberPagination(CursorPagination):
    cursor_query_param = 'cursor'
    page_size = 2
    ordering = '-id' # 倒序,也就是越新的文章,我们希望在第一页展示

接下来直接访问http://127.0.0.1:8888/api/v1/article/就能看到页面返回了样式。但是可能会有人说这咋反问下一页呢?别急。接下来还要在说一个关于分页的返回方法

使用get_paginated_response返回更详细的分页数据

如果你是使用前面两种的分页模式PageNumberPaginationLimitOffsetPagination那么你不使用这个函数也没有问题,但是如果你是使用CursorPagination,由于他对下一页、上一页进行了转码处理,所以就需要使用这个方法来查看下一页、上一页的具体页码到底是什么了。 先看看我们的函数类

class ArticleView(APIView):
    def get(self,request,*args,**kwargs):
        article_list_obj = article_models.Article.objects.all() # 获取所有文章
        pg = MyPageNumberPagination()
        pager_article = pg.paginate_queryset(queryset=article_list_obj,request=request,view=self) # 分页
        ser = serializers.ArticleSerializer(instance=pager_article,many=True) # 直接序列化分页结果即可
        return Response(ser.data) 

之前我们都是使用DRFResponse直接返回的,其实pagination也提供了一个额外的渲染器给我们使用。在每个类的内部都有一个get_paginated_response函数。他会给我们的数据增加几个额外的项。源码如下

    def get_paginated_response(self, data):
        return Response(OrderedDict([
            ('count', self.page.paginator.count),
            ('next', self.get_next_link()),
            ('previous', self.get_previous_link()),
            ('results', data)
        ]))

那么这就简单了,我们对我们的类视图进行一下改造,将return Response(ser.data)改成return pg.get_paginated_response(ser.data)即可。 然后什么都不用做,在去看看页面的变化。

# PageNumberPagination 、LimitOffsetPagination 会增加总条数、上一页、下一页的地址
    "count": 7,
    "next": "http://127.0.0.1:8888/api/v1/article/?page=3",
    "previous": "http://127.0.0.1:8888/api/v1/article/",

# CursorPagination 会增加上一页、下一页的地址
    "next": "http://127.0.0.1:8888/api/v1/article/?cursor=cD00",
    "previous": "http://127.0.0.1:8888/api/v1/article/?cursor=cj0xJnA9Mw%3D%3D",