文章目录

  • 一、前后端分离
    • 1. 开发模式
    • 2. 前后端分离的优势
    • 3. 为什么要有前后端分离
    • 4. 前后端分离的缺点
  • 二、RESTful API
    • 1. RESTful API设计规范
  • 三、快速入手
  • 四、序列化
    • 1. 普通序列化
      • 1.1 模型类
      • 1.2 序列化
      • 1.3 参数约束
      • 1.4 利用反序列化生成模型对象
      • 1.5 利用序列化生成JSON数据
    • 2. 模型序列化ModelSerializer
      • 2.1 序列化
      • 2.2 编写视图
      • 2.3 编写子路由
      • 2.4 编写根路由
      • 2.5 注意
    • 3. 模型序列化(高级)
      • 3.1 模型类
      • 3.2 序列化
      • 3.3 视图
      • 3.4 路由
      • 3.5 StringRelatedField
        • 3.5.1 序列化
      • 3.6 PrimaryKeyRelatedField
        • 3.6.1 序列化
      • 3.7 HyperlinkedRelatedField
        • 3.7.1 报错
        • 3.7.2 添加根路由、子路由别名
      • 3.8 SlugRelatedField
        • 3.8.1 序列化
      • 3.9 HyperlinkedIdentityField
        • 3.9.1 序列化
      • 3.10 HyperlinkedModelSerializer
        • 3.10.1 序列化
      • 3.11 序列化嵌套
        • 3.11.1 序列化
        • 3.11.2 执行结果
      • 3.12 depth
        • 3.12.1 序列化
        • 3.12.2 执行结果
      • 3.13 SerializerMethodField
        • 3.13.1 序列化
        • 3.13.2 执行结果
      • 3.14 source
        • 3.14.1 序列化
        • 3.14.2 执行结果
        • 3.14.3 将返回的对象转为具体内容
        • 3.14.4 利用source实现可读可写
      • 3.15 to_representation方法
        • 3.15.1 模型类
        • 3.15.2 序列化
        • 3.15.3 路由
    • 4. 序列化验证
      • 4.1 模型类
      • 4.2 视图
      • 4.3 路由
      • 4.4 序列化
      • 4.5 Validators(了解)
  • 五、请求和响应
    • 1. 请求
    • 2. 响应
    • 3. 状态码
    • 4. API视图
      • 4.1 基于函数视图的@api_view装饰器
      • 4.2 基于类视图的APIView类
    • 5. 路由
  • 六、类视图
    • 1. mixins
    • 2. 路由
    • 3. 使用通用的基于类的视图
  • 七、认证和权限
    • 1. 创建用户
    • 2. 快速体验
      • 2.1 模型类
      • 2.2 视图
      • 2.3 序列化类
      • 2.4 路由
      • 2.5 测试
      • 2.6 总结
    • 3. 权限
      • 3.1 添加权限
      • 3.2 测试
    • 4. 认证
      • 4.1 BasicAuthentication
        • 4.1.1 路由
        • 4.1.2 视图
        • 4.1.3 小结
      • 4.2 TokenAuthentication
        • 4.2.1 配置
        • 4.2.2 路由
        • 4.2.3 视图
        • 4.2.4 测试
      • 4.3 SessionAuthentication
        • 4.3.1 视图
        • 4.3.2 测试
      • 4.4 自定义认证
        • 4.4.1 模型类
        • 4.4.2 视图
        • 4.4.3 自定义权限
        • 4.4.4 路由
        • 4.4.5 测试
      • 4.5 JWT验证
        • 4.5.1 安装
        • 4.5.2 注册
        • 4.5.3 配置
        • 4.5.4 路由
        • 4.5.5 视图
        • 4.5.6 测试
        • 4.5.7 JWT权限验证
      • 4.6 JWT优缺点
  • 八、频率
    • 1. 内置频率类
    • 2. 配置
    • 3. 视图
  • 九、版本控制
    • 1. 五种控制方案
    • 2. 视图
    • 3. 路由
    • 4. 配置
    • 5. 通用的基于类的视图中的用法
      • 5.1 模型
      • 5.2 序列化
      • 5.3 视图
      • 5.4 路由
      • 5.5 测试
  • 十、分页
    • 1. 分页方式
    • 2. 普通分页
    • 3. 切割分页
    • 4. 加密分页
    • 5. 配置

一、前后端分离

1. 开发模式

在Web开发模式中,有两种开发模式

  • 前后端不分离
  • 前后端分离

2. 前后端分离的优势

在前后端不分离模式中,前端页面看到的效果都是由后端控制,由后端渲染页面或重定向,也就是后端需要控制前端的展示,前端与后端的耦合度很高。

这种应用模式比较适合纯网页应用,但是当后端对接App时,App可能并不需要后端返回一个HTML网页,而仅仅是数据本身,所以后端原本返回网页的接口不再适用于前端App应用,为了对接App后端还需再开发一套接口。

在前后端分离的应用模式中,后端仅返回前端所需的数据,不再渲染HTML页面,不再控制前端的效果。至于前端用户看到什么效果,从后端请求的数据如何加载到前端中,都由前端自己决定,网页有网页的处理方式,App有App的处理方式,但无论哪种前端,所需的数据基本相同,后端仅需开发一套逻辑对外提供数据即可。

在前后端分离的应用模式中,前端与后端的耦合度相对较低。

在前后端分离的应用模式中,我们通常将后端开发的每个视图都称为一个接口,或者API,前端通过访问接口来对数据进行增删改查。

3. 为什么要有前后端分离

  • 多端适应
  • 前后端开发职责不清
    • 模板到低是前端写还是后端写?如果前端写,前端就要懂后端模板语言
    • 如果后端写,那么后端就要懂前段html, css, js甚至更多更多
  • 开发效率问题,前后端相互等待
    • 前端在写的时候,就希望后端全部写好自己再写
    • 后端在写的时候,就希望前端全部写好自己再写
  • 前端一直配合着后端,能力受限(搞来搞去写静态页面,天天给后台写模板)
  • 后台开发语言和模板高度耦合,导致开发语言依赖(一旦用python开发,以后要换成java或者其他语言)

4. 前后端分离的缺点

  • 前后端学习门槛增加(比如后台很多模板语法控制前端展示,但是分离以后,需要前端自己去实现,增加前端成本,对后台来说,后端要满足规范)

  • 数据依赖导致文档重要性增加

    • 文档是否详细
    • 是否及时更新
    • 修改要及时通知其他端
  • 前端工作量加大

  • SEO(搜索引擎优化)的难度增加(都是Ajax,像一些初级爬虫全部挡在外面,比如一些搜索引擎,这样你的排名就不靠前了)

二、RESTful API

1. RESTful API设计规范

  • 域名

应该尽量将API部署在专用域名之下

https://api.example.com

如果确定API很简单,不会有进一步扩展,可以考虑放在主域名下。

https://example.com/api/
  • 协议

推荐使用HTTPS

  • 版本

应该将API的版本号放入URL

http://www.example.com/app/v1.0/appleshttp://www.example.com/app/v1.1/appleshttp://www.example.com/app/v2.0/apples

比如,v1.0版本返回

{"name": "lucy"
}

v2.0版本返回

{"name": "lucy","age": 18
}
  • 路径

对于一个简洁结构,你应该始终用名词。 此外,利用的HTTP方法可以分离网址中的资源名称的操作。

推荐用名词+复数

https://xxx.com/api/v2.0/students/
GET  /products       将返回所有产品清单
POST    /products       将产品新建到集合
GET     /products/4     将获取产品4
PATCH   /products/4     将更新产品4(部分属性更新)
PUT     /products/4     将更新产品4(全部属性更新)
  • HTTP动词

对数据的操作CRUD

create, read, update, delete

GET--->获取资源https://xxx.com/api/v2.0/students/
POST--->新增资源https://xxx.com/api/v2.0/students/
PUT--->更新资源https://xxx.com/api/v2.0/students/1/
PATCH--->部分更新https://xxx.com/api/v2.0/students/1/
DELETE--->删除资源https://xxx.com/api/v2.0/students/1/
比如,只更新name属性,使用PUT
{"id": 1,"name": "lucy","age": 20
}
使用PATCH时
{"id": 1,"age": 20
}
请求方法 请求地址 后端操作
GET /students 获取所有学生
POST /students 增加学生
GET /students/1 获取编号为1的学生
PUT /students/1 更新编号为1的学生(全部属性)
DELETE /students/1 删除编号为1的学生
PATCH /students/1 更新编号为1的学生(部分属性)
  • 过滤信息
https://xxx.com/api/v2.0/students/?page=2
?limit=10   指定返回记录的数量
?offset=10 指定返回记录的开始位置
?page=2&per_page=100  指定第几页,以及每页的记录数
?sortby=name&order=asc    指定返回结果按照哪个属性排序,以及排序顺序
?animal_type_id=1      指定筛选条件
  • 状态码
移动端要求这样的格式
{"code": 1,"msg": "xxx","data": {}
}
200 OK - [GET]:服务器成功返回用户请求的数据。
201 CREATED - [POST/PUT/PATCH]:用户新建或修改数据成功。
202 Accepted - []:表示一个请求已经进入后台排队(异步任务)。
204 NO CONTENT - [DELETE]:用户删除数据成功。
400 INVALID REQUEST - [POST/PUT/PATCH]:用户发出的请求有错误,服务器没有进行新建或修改数据的操作。
401 Unauthorized - []:表示用户没有权限(令牌、用户名、密码错误)。
403 Forbidden - []:表示用户得到授权(与401错误相对),但是访问是被禁止的。
404 NOT FOUND - []:用户发出的请求针对的是不存在的记录,服务器没有进行操作,该操作是幂等的。
406 Not Acceptable - [GET]:用户请求的格式不可得(比如用户请求JSON格式,但是只有XML格式)。
410 Gone -[GET]:用户请求的资源被永久删除,且不会再得到的。
422 Unprocesable entity - [POST/PUT/PATCH]:当创建一个对象时,发生一个验证错误。
500 INTERNAL SERVER ERROR - [*]:服务器发生错误,用户将无法判断发出的请求是否成功。
  • 错误处理
移动端要求这样的格式
{"code": 1,"msg": "xxx","data": {}
}
{error:"Invalid API key"
}
  • 返回结果

返回的数据都是json

GET      /collection             返回资源对象的列表(数组)
GET     /collection/resource    返回单个资源对象
POST    /collection             返回新生成的资源对象
PUT     /collection/resource    返回完整的资源对象
PATCH   /collection/resource    返回完整的资源对象
DELETE  /collection/resource    返回一个空文档

三、快速入手

四、序列化

1. 普通序列化

1.1 模型类
from django.db import models# Create your models here.
class Article(models.Model):title = models.CharField(verbose_name='标题', max_length=100)vnum = models.IntegerField(verbose_name='浏览量')content = models.TextField(verbose_name='内容')
1.2 序列化

最普通的序列化,需要自己手写需要序列化的字段。

from rest_framework import serializers
from .models import Article# 普通的序列化
class ArticleSerializer(serializers.Serializer):id = serializers.IntegerField(read_only=True)vnum = serializers.IntegerField()content = serializers.CharField(max_length=1000)title = serializers.CharField(required=True, max_length=100)def create(self, validated_data):return Article.objects.create(**validated_data)def update(self, instance, validated_data):instance.title = validated_data.get('title', instance.title)  # 没取到用原来的instance.vnum = validated_data.get('vnum', instance.vnum)instance.content = validated_data.get('content', instance.content)instance.save()return instance
1.3 参数约束
read_only: True表示不允许用户自己上传,只能用于api的输出。
write_only: 与read_only对应
required: 顾名思义,就是这个字段是否必填。
allow_null/allow_blank: 是否允许为NULL/空。
error_messages: 出错时,信息提示。
name = serializers.CharField(required=True, min_length=6,error_messages={'min_length': '名字不能小于6个字符','required': '请填写名字'})
label: 字段显示设置,如 label=’验证码’
help_text: 在指定字段增加一些提示文字,这两个字段作用于api页面比较有用。
style: 说明字段的类型,这样看可能比较抽象,看下面例子:
# 在api页面,输入密码就会以*显示
password = serializers.CharField(style={'input_type': 'password'})
# 会显示选项框
color_channel = serializers.ChoiceField(choices=['red', 'green', 'blue'],style={'base_template': 'radio.html'})
validators: 自定义验证逻辑。
1.4 利用反序列化生成模型对象

当我们反序列化的时候,要用data关键字

当我们反序列化的时候,会帮我们校验数据是否合法

在Terminal中打开执行命令python manage.py shell,打开shell

>>> from app03.serializers import ArticleSerializer
>>> from rest_framework.renderers import JSONRenderer>>> d = {"title":"这是一个测试标签","vnum":11,"content":"哈哈哈哈"}
>>> ser = ArticleSerializer(data=d)
>>> ser.is_valid()  # 验证数据是否正确,比如少写属性
True
>>> ser.save()  # 保存数据
<Article: Article object (1)>
1.5 利用序列化生成JSON数据

当我们序列化的时候,要用instance关键字

当序列化的时候,如果是多个数据,需要加many=True,单个对象不需要

>>> from app03.models import *
>>> from rest_framework.renderers import JSONRenderer
>>> from app03.serializers import ArticleSerializer# 单个对象序列化
>>> art = Article.objects.get(id=1)
>>> ser = ArticleSerializer(art)
>>> ser.data
{'id': 1, 'title': '这是一个测试标签', 'vnum': 11, 'content': '哈哈哈哈'}
>>> JSONRenderer().render(ser.data).decode('utf-8')
'{"id":1,"title":"这是一个测试标签","vnum":11,"content":"哈哈哈哈"}'# 多个对象序列化
>>> arts = Article.objects.all()
>>> ser = ArticleSerializer(instance=arts,many=True)
>>> ser.data
[OrderedDict([('id', 1), ('title', '这是一个测试标签'), ('vnum', 11), ('content'
, '哈哈哈哈')]), OrderedDict([('id', 2), ('title', '111'), ('vnum', 100), ('cont
ent', 'hahahahaha')])]
>>> JSONRenderer().render(ser.data).decode('utf-8')
'[{"id":1,"title":"这是一个测试标签","vnum":11,"content":"哈哈哈哈"},{"id":2,"ti
tle":"111","vnum":100,"content":"hahahahaha"}]'

2. 模型序列化ModelSerializer

ModelSerializer默认帮我们实现了创建和更新方法,简化了我们的操作,当然如果你想自己写,可以重写它。其余使用方法跟普通的序列化一样。

2.1 序列化
class ArticleSerializer(serializers.ModelSerializer):class Meta:model = Articlefields = ('id', 'title', 'vnum', 'content')  # 三者取一# exclude = ()  # 表示不返回字段  三者取一# fields = '__all__'  # 表示所有字段  三者取一# read_only_fields = ()  # 设置只读字段 不接受用户修改
2.2 编写视图
from django.http import JsonResponse
from django.shortcuts import render# Create your views here.
from django.views.decorators.csrf import csrf_exempt
from rest_framework.parsers import JSONParserfrom app03.serializers import ArticleSerializer
from app03.models import *@csrf_exempt
def article_list(request):if request.method == 'GET':arts = Article.objects.all()ser = ArticleSerializer(instance=arts, many=True)return JsonResponse({'status': 200, 'data': ser.data})elif request.method == 'POST':data = JSONParser().parse(request)  # 把前端传过来的json数据转为python里面的数据类型ser = ArticleSerializer(data=data)if ser.is_valid():ser.save()return JsonResponse({'status': 201, 'data': ser.data})return JsonResponse({'status': 400, 'data': ser.errors})@csrf_exempt
def article_detail(request, pk):try:art = Article.objects.get(pk=pk)except Article.DoesNotExist as e:return JsonResponse({'msg': "未获取到pk值", 'status': 404})if request.method == 'GET':ser = ArticleSerializer(instance=art)return JsonResponse({'status': 200, 'data': ser.data})elif request.method == 'PUT':data = JSONParser().parse(request)ser = ArticleSerializer(instance=art, data=data)if ser.is_valid():ser.save()return JsonResponse({'status': 201, 'data': ser.data})return JsonResponse({'status': 400, 'data': ser.errors})elif request.method == 'PATCH':data = JSONParser().parse(request)ser = ArticleSerializer(instance=art, data=data, partial=True)if ser.is_valid():ser.save()return JsonResponse({'status': 201, 'data': ser.data})return JsonResponse({'status': 400, 'data': ser.errors})elif request.method == 'DELETE':art.delete()return JsonResponse({'msg': '删除成功', 'status': 204})
2.3 编写子路由
from django.urls import pathfrom app03 import viewsurlpatterns = [path('articles/', views.article_list, name='article_list'),  # 获取或创建path('articles/<int:pk>/', views.article_detail, name='article_detail'),  # 查找、更新、删除]
2.4 编写根路由
urlpatterns = [path('admin/', admin.site.urls),path('api/app03/', include('app03.urls')),
]
2.5 注意

当请求方法为PATCH,序列化需要加 partial=True,让支持增量更新。

路由里面的参数跟视图里面的参数一定要一样,因为是关键字传参。上述体现在pk的使用。

3. 模型序列化(高级)

3.1 模型类
from django.db import models# Create your models here.class Category(models.Model):name = models.CharField(verbose_name='分类', max_length=10)def __str__(self):return self.nameclass Article(models.Model):title = models.CharField(verbose_name='标题', max_length=100)vnum = models.IntegerField(verbose_name='浏览量')content = models.TextField(verbose_name='内容')category = models.ForeignKey(to=Category, on_delete=models.CASCADE, related_name='articles')  # related_name 反向查找def __str__(self):return self.title
3.2 序列化
class ArticleSerializer(serializers.ModelSerializer):class Meta:model = Articlefields = ('id', 'title', 'vnum', 'content', 'category')class CategorySerializer(serializers.ModelSerializer):class Meta:model = Categoryfields = '__all__'
3.3 视图
@csrf_exempt
def category_list(request):if request.method == 'GET':cats = Category.objects.all()ser = CategorySerializer(instance=cats, many=True)return JsonResponse({'status': 200, 'data': ser.data})elif request.method == 'POST':data = JSONParser().parse(request)  # 把前端传过来的json数据转为python里面的数据类型ser = CategorySerializer(data=data)if ser.is_valid():ser.save()return JsonResponse({'status': 201, 'data': ser.data})return JsonResponse({'status': 400, 'data': ser.errors})@csrf_exempt
def category_detail(request, pk):try:art = Category.objects.get(pk=pk)except Category.DoesNotExist as e:return JsonResponse({'msg': "未获取到pk值", 'status': 404})if request.method == 'GET':ser = CategorySerializer(instance=art)return JsonResponse({'status': 200, 'data': ser.data})elif request.method == 'PUT':data = JSONParser().parse(request)ser = CategorySerializer(instance=art, data=data)if ser.is_valid():ser.save()return JsonResponse({'status': 201, 'data': ser.data})return JsonResponse({'status': 400, 'data': ser.errors})elif request.method == 'PATCH':data = JSONParser().parse(request)ser = CategorySerializer(instance=art, data=data, partial=True)if ser.is_valid():ser.save()return JsonResponse({'status': 201, 'data': ser.data})return JsonResponse({'status': 400, 'data': ser.errors})elif request.method == 'DELETE':art.delete()return JsonResponse({'msg': '删除成功', 'status': 204})
3.4 路由
from django.urls import pathfrom app03 import viewsurlpatterns = [path('articles/', views.article_list, name='article_list'),path('articles/<int:pk>/', views.article_detail, name='article_detail'),path('categorys/', views.category_list, name='category_list'),path('categorys/<int:pk>/', views.category_detail, name='category_detail'),]
3.5 StringRelatedField

StringRelatedField 将返回一个对应关系 model 的 __str__() 方法的返回值。

3.5.1 序列化
class ArticleSerializer(serializers.ModelSerializer):category = serializers.StringRelatedField()class Meta:model = Articlefields = '__all__'class CategorySerializer(serializers.ModelSerializer):articles = serializers.StringRelatedField(many=True)  # 不写,默认返回id,写了返回__str__指定的字段class Meta:model = Categoryfields = ('id', 'name', 'articles')  # articles是反向引用,隐形属性
3.6 PrimaryKeyRelatedField

使用 PrimaryKeyRelatedField 将返回一个对应关系 model 的主键。

参数:

  • queryset 用于在验证字段输入时模型实例查找。关系必须明确设置 queryset,或设置 read_only = True
  • many 如果是对应多个的关系,就设置为 True
  • allow_null 如果设置为 True,则该字段将接受 None 的值或为空的关系的空字符串。默认为 False
  • pk_field 设置为一个字段以控制主键值的序列化/反序列化。例如,pk_field = UUIDField(format =‘hex’) 将UUID主键序列化为紧凑的十六进制表示。
3.6.1 序列化
class ArticleSerializer(serializers.ModelSerializer):category = serializers.PrimaryKeyRelatedField(read_only=True)class Meta:model = Articlefields = '__all__'class CategorySerializer(serializers.ModelSerializer):articles = serializers.PrimaryKeyRelatedField(read_only=True, many=True)class Meta:model = Categoryfields = ('id', 'name', 'articles')  # articles是反向引用,隐形属性
3.7 HyperlinkedRelatedField

使用 HyperlinkedRelatedField 将返回一个超链接,该链接指向对应关系 model 的详细数据,view-name 是必选参数,也就是路由中 name 的值,为对应的视图生成超链接。

参数:

  • view_name 用作关系目标的视图名称。如果使用的是标准路由器类,那么它的格式为 <modelname>-detail 的字符串
  • queryset 验证字段输入时用于模型实例查询的查询器。关系必须明确设置 queryset,或设置 read_only = True
  • many 如果应用于多对多关系,则应将此参数设置为 True
  • allow_null 如果设置为 True,则该字段将接受 None 的值或为空的关系的空字符串。默认为 False
  • lookup_field 应该用于查找的目标上的字段。应该对应于引用视图上的 URL 关键字参数。默认值为 pk
  • lookup_url_kwarg 与查找字段对应的 URL conf 中定义的关键字参数的名称。默认使用与 lookup_field 相同的值
  • format 如果使用 format 后缀,超链接字段将对目标使用相同的 format 后缀,除非使用 format 参数进行覆盖。
from django.http import JsonResponse
from django.shortcuts import render# Create your views here.
from django.views.decorators.csrf import csrf_exempt
from rest_framework.parsers import JSONParserfrom app03.serializers import ArticleSerializer, CategorySerializer
from app03.models import *@csrf_exempt
def article_list(request):if request.method == 'GET':arts = Article.objects.all()ser = ArticleSerializer(instance=arts, many=True, context={'request': request})return JsonResponse({'status': 200, 'data': ser.data})elif request.method == 'POST':data = JSONParser().parse(request)  # 把前端传过来的json数据转为python里面的数据类型ser = ArticleSerializer(data=data, context={'request': request})if ser.is_valid():ser.save()return JsonResponse({'status': 201, 'data': ser.data})return JsonResponse({'status': 400, 'data': ser.errors})@csrf_exempt
def article_detail(request, pk):try:art = Article.objects.get(pk=pk)except Article.DoesNotExist as e:return JsonResponse({'msg': "未获取到pk值", 'status': 404})if request.method == 'GET':ser = ArticleSerializer(instance=art, context={'request': request})return JsonResponse({'status': 200, 'data': ser.data})elif request.method == 'PUT':data = JSONParser().parse(request)ser = ArticleSerializer(instance=art, data=data, context={'request': request})if ser.is_valid():ser.save()return JsonResponse({'status': 201, 'data': ser.data})return JsonResponse({'status': 400, 'data': ser.errors})elif request.method == 'PATCH':data = JSONParser().parse(request)ser = ArticleSerializer(instance=art, data=data, partial=True, context={'request': request})if ser.is_valid():ser.save()return JsonResponse({'status': 201, 'data': ser.data})return JsonResponse({'status': 400, 'data': ser.errors})elif request.method == 'DELETE':art.delete()return JsonResponse({'msg': '删除成功', 'status': 204})@csrf_exempt
def category_list(request):if request.method == 'GET':cats = Category.objects.all()ser = CategorySerializer(instance=cats, many=True, context={'request': request})return JsonResponse({'status': 200, 'data': ser.data})elif request.method == 'POST':data = JSONParser().parse(request)  # 把前端传过来的json数据转为python里面的数据类型ser = CategorySerializer(data=data, context={'request': request})if ser.is_valid():ser.save()return JsonResponse({'status': 201, 'data': ser.data})return JsonResponse({'status': 400, 'data': ser.errors})@csrf_exempt
def category_detail(request, pk):try:art = Category.objects.get(pk=pk)except Category.DoesNotExist as e:return JsonResponse({'msg': "未获取到pk值", 'status': 404})if request.method == 'GET':ser = CategorySerializer(instance=art, context={'request': request})return JsonResponse({'status': 200, 'data': ser.data})elif request.method == 'PUT':data = JSONParser().parse(request)ser = CategorySerializer(instance=art, data=data, context={'request': request})if ser.is_valid():ser.save()return JsonResponse({'status': 201, 'data': ser.data})return JsonResponse({'status': 400, 'data': ser.errors})elif request.method == 'PATCH':data = JSONParser().parse(request)ser = CategorySerializer(instance=art, data=data, partial=True, context={'request': request})if ser.is_valid():ser.save()return JsonResponse({'status': 201, 'data': ser.data})return JsonResponse({'status': 400, 'data': ser.errors})elif request.method == 'DELETE':art.delete()return JsonResponse({'msg': '删除成功', 'status': 204})
3.7.1 报错
AssertionError: `HyperlinkedRelatedField` requires the request in the serializer context. Add `context={'request': request}` when instantiating theserializer.

在所有的XXXSerializer中添加上下文 context={‘request’: request},比如:

ArticleSerializer(instance=arts, many=True, context={'request': request})
3.7.2 添加根路由、子路由别名

根路由

urlpatterns = [path('admin/', admin.site.urls),path('api/app03/', include(('app03.urls', 'app03'), namespace='app03')),]

子路由

urlpatterns = [path('articles/', views.article_list, name='article-list'),path('articles/<int:pk>/', views.article_detail, name='article-detail'),path('categorys/', views.category_list, name='category-list'),path('categorys/<int:pk>/', views.category_detail, name='category-detail'),]

模型序列化类中,使用 view_name=‘根别名:子别名’

class ArticleSerializer(serializers.ModelSerializer):category = serializers.HyperlinkedRelatedField(view_name='app03:category-detail',read_only=True,)class Meta:model = Articlefields = '__all__'class CategorySerializer(serializers.ModelSerializer):articles = serializers.HyperlinkedRelatedField(view_name='app03:article-detail',read_only=True,many=True)class Meta:model = Categoryfields = ('id', 'name', 'articles')  # articles是反向引用,隐形属性
3.8 SlugRelatedField

使用 SlugRelatedField 将返回一个指定对应关系 model 中的字段,需要参数 slug_field 中指定字段名称。

参数:

  • slug_field 应该用于表示目标的字段。这应该是唯一标识任何给定实例的字段。例如 username 。这是必选参数
  • queryset 验证字段输入时用于模型实例查询的查询器。 关系必须明确设置 queryset,或设置 read_only = True
  • many 如果应用于多对多关系,则应将此参数设置为 True
  • allow_null 如果设置为 True,则该字段将接受 None 的值或为空的关系的空字符串。默认为 False
3.8.1 序列化
class ArticleSerializer(serializers.ModelSerializer):category = serializers.SlugRelatedField(read_only=True,slug_field='name',)class Meta:model = Articlefields = '__all__'class CategorySerializer(serializers.ModelSerializer):articles = serializers.SlugRelatedField(read_only=True,slug_field='vnum',many=True)class Meta:model = Categoryfields = ('id', 'name', 'articles')  # articles是反向引用,隐形属性
3.9 HyperlinkedIdentityField

使用 HyperlinkedIdentityField 将返回指定 view-name 的超链接的字段。

参数:

  • view_name 应该用作关系目标的视图名称。如果您使用的是标准路由器类,则它将是格式为 <model_name>-detail 的字符串。必选参数
  • lookup_field 应该用于查找的目标上的字段。应该对应于引用视图上的 URL 关键字参数。默认值为 pk
  • lookup_url_kwarg 与查找字段对应的 URL conf 中定义的关键字参数的名称。默认使用与 lookup_field 相同的值
  • format 如果使用 format 后缀,超链接字段将对目标使用相同的 format 后缀,除非使用 format 参数进行覆盖
3.9.1 序列化
class ArticleSerializer(serializers.ModelSerializer):category = serializers.HyperlinkedIdentityField(view_name='app03:category-detail',)class Meta:model = Articlefields = '__all__'class CategorySerializer(serializers.ModelSerializer):articles = serializers.HyperlinkedIdentityField(view_name='app03:article-detail',many=True)class Meta:model = Categoryfields = ('id', 'name', 'articles')  # articles是反向引用,隐形属性
3.10 HyperlinkedModelSerializer

HyperlinkedModelSerializer 类与 ModelSerializer 类相似,只不过它使用超链接来表示关系而不是主键。

默认情况下,序列化器将包含一个 url 字段而不是主键字段。
url 字段将使用 HyperlinkedIdentityField 序列化器字段来表示,并且模型上的任何关系都将使用 HyperlinkedRelatedField 序列化器字段来表示。

3.10.1 序列化
class ArticleSerializer(serializers.HyperlinkedModelSerializer):class Meta:model = Articlefields = '__all__'extra_kwargs = {'url': {'view_name': 'app03:article-detail', 'lookup_field': 'pk'},'category': {'view_name': 'app03:category-detail', 'lookup_field': 'pk'}}class CategorySerializer(serializers.HyperlinkedModelSerializer):class Meta:model = Categoryfields = ('id', 'name', 'articles', 'url')  # articles是反向引用,隐形属性extra_kwargs = {'url': {'view_name': 'app03:category-detail', 'lookup_field': 'pk'},'articles': {'view_name': 'app03:article-detail', 'lookup_field': 'pk'}}
3.11 序列化嵌套

顾名思义就是序列化里面有另一个序列化类

3.11.1 序列化
class ArticleSerializer(serializers.ModelSerializer):class Meta:model = Articlefields = '__all__'class CategorySerializer(serializers.ModelSerializer):articles = ArticleSerializer(many=True)class Meta:model = Categoryfields = ('id', 'name', 'articles')  # 这里的articles与前面的articles对应
3.11.2 执行结果

http://127.0.0.1:8000/api/app03/categorys/

{"status": 200,"data": [{"id": 1,"name": "分类一","articles": [{"id": 1,"title": "这是一个测试标签","vnum": 11,"content": "哈哈哈哈","category": 1},{"id": 2,"title": "标题000","vnum": 100,"content": "hehehehehe","category": 1}]},{"id": 2,"name": "分类二","articles": [{"id": 3,"title": "标题111","vnum": 100,"content": "hahahahaha","category": 2}]}]
}
3.12 depth

这个字段可以用来深度遍历

3.12.1 序列化
class ArticleSerializer(serializers.ModelSerializer):class Meta:model = Articlefields = '__all__'depth = 2class CategorySerializer(serializers.ModelSerializer):class Meta:model = Categoryfields = ('id', 'name', 'articles')
3.12.2 执行结果

http://127.0.0.1:8000/api/app03/articles/

{"status": 200,"data": [{"id": 1,"title": "这是一个测试标签","vnum": 11,"content": "哈哈哈哈","category": {"id": 1,"name": "分类一"}},{"id": 2,"title": "标题000","vnum": 100,"content": "hehehehehe","category": {"id": 1,"name": "分类一"}},{"id": 3,"title": "标题111","vnum": 100,"content": "hahahahaha","category": {"id": 2,"name": "分类二"}}]
}
3.13 SerializerMethodField

通过这个属性我们可以自定义一些属性。

自定义属性必须实现 get_自定义属性名 的方法,obj 代表当前 被序列化的模型的 对象。

比如有这样的需求,统计每个分类下有多少篇文章

3.13.1 序列化
class ArticleSerializer(serializers.ModelSerializer):class Meta:model = Articlefields = '__all__'class CategorySerializer(serializers.ModelSerializer):count = serializers.SerializerMethodField()class Meta:model = Categoryfields = ('id', 'name', 'articles', 'count')# 自定义属性必须实现 get_自定义属性名 的方法# obj代表当前 被序列化的模型的对象def get_count(self, obj):return obj.articles.count()
3.13.2 执行结果

http://127.0.0.1:8000/api/app03/categorys/

{"status": 200,"data": [{"id": 1,"name": "分类一","articles": [1,2],"count": 2},{"id": 2,"name": "分类二","articles": [3],"count": 1}]
}
3.14 source

序列化的时候指定数据源

3.14.1 序列化
class ArticleSerializer(serializers.ModelSerializer):category = serializers.CharField(source='category.name')class Meta:model = Articlefields = '__all__'class CategorySerializer(serializers.ModelSerializer):arts = serializers.CharField(source='articles.all')# arts = serializers.CharField(source='article_set.all')  # 如果没有related_nameclass Meta:model = Categoryfields = ('id', 'name', 'articles', 'arts')
3.14.2 执行结果

http://127.0.0.1:8000/api/app03/categorys/

{"status": 200,"data": [{"id": 1,"name": "分类一","articles": [1,2],"arts": "<QuerySet [<Article: 这是一个测试标签>, <Article: 标题000>]>"},{"id": 2,"name": "分类二","articles": [3],"arts": "<QuerySet [<Article: 标题111>]>"}]
}
3.14.3 将返回的对象转为具体内容
class MyCharField(serializers.CharField):def to_representation(self, value):data_list = []for val in value:data_list.append({'title': val.title, 'content': val.content})return data_listclass ArticleSerializer(serializers.ModelSerializer):category = serializers.CharField(source='category.name')class Meta:model = Articlefields = '__all__'class CategorySerializer(serializers.ModelSerializer):# arts = serializers.CharField(source='articles.all')arts = MyCharField(source='articles.all')class Meta:model = Categoryfields = ('id', 'name', 'articles', 'arts')

http://127.0.0.1:8000/api/app03/categorys/

{"status": 200,"data": [{"id": 1,"name": "分类一","articles": [1,2],"arts": [{"title": "这是一个测试标签","content": "哈哈哈哈"},{"title": "标题000","content": "hehehehehe"}]},{"id": 2,"name": "分类二","articles": [3],"arts": [{"title": "标题111","content": "hahahahaha"}]}]
}
3.14.4 利用source实现可读可写
from collections import OrderedDictclass ChoiceDisplayField(serializers.Field):"""Custom ChoiceField serializer field."""def __init__(self, choices, **kwargs):"""init."""self._choices = OrderedDict(choices)super(ChoiceDisplayField, self).__init__(**kwargs)# 返回可读性良好的字符串而不是 1,-1 这样的数字def to_representation(self, obj):"""Used while retrieving value for the field."""return self._choices[obj]def to_internal_value(self, data):"""Used while storing value for the field."""for i in self._choices:# 这样无论用户POST上来但是CHOICES的 Key 还是Value 都能被接受if i == data or self._choices[i] == data:return iraise serializers.ValidationError("Acceptable values are {0}.".format(list(self._choices.values())))
3.15 to_representation方法

序列化器的每个字段实际都是由该字段类型的to_representation方法决定格式的,可以通过重写该方法来决定格式。解决序列化和反序列化之间的关系。

3.15.1 模型类
from django.db import models# Create your models here.class Category(models.Model):name = models.CharField(verbose_name='分类', max_length=10)def __str__(self):return self.nameclass Tag(models.Model):name = models.CharField(verbose_name='标签名字', max_length=10)created_time = models.DateTimeField()def __str__(self):return self.nameclass Article(models.Model):title = models.CharField(verbose_name='标题', max_length=100)vnum = models.IntegerField(verbose_name='浏览量')content = models.TextField(verbose_name='内容')category = models.ForeignKey(to=Category, on_delete=models.CASCADE, related_name='articles')tags = models.ManyToManyField(to=Tag, related_name='articles')def __str__(self):return self.title
3.15.2 序列化
class ArticleSerializer(serializers.ModelSerializer):class Meta:model = Articlefields = '__all__'def to_representation(self, instance):representation = super(ArticleSerializer, self).to_representation(instance)representation['category'] = CategorySerializer(instance.category).datarepresentation['tags'] = TagSerializer(instance.tags, many=True).datareturn representationclass CategorySerializer(serializers.ModelSerializer):class Meta:model = Categoryfields = ('id', 'name', 'articles')class TagSerializer(serializers.ModelSerializer):created_time = serializers.DateTimeField(format='%Y-%m-%d %H:%M:%S')class Meta:model = Tagfields = '__all__'
3.15.3 路由
from django.urls import pathfrom app03 import viewsurlpatterns = [path('articles/', views.article_list, name='article-list'),path('articles/<int:pk>/', views.article_detail, name='article-detail'),path('categorys/', views.category_list, name='category-list'),path('categorys/<int:pk>/', views.category_detail, name='category-detail'),path('tags/', views.tag_list, name='tag-list'),path('tags/<int:pk>/', views.tag_detail, name='tag-detail'),]

4. 序列化验证

一般前后端分离的时候,我们都会校验前端的参数时候合法。如果我们ModelSerializer话,因为它本身已经帮我们写好create方法,所以我们基本不需要再写验证。但是一些特殊的我们就需要重写或者自己写验证方法。

4.1 模型类
from django.db import models# Create your models here.
class User(models.Model):GENDERS = ((1, '男'), (2, "女"))name = models.CharField(max_length=10, verbose_name='名字')phone = models.CharField(max_length=11, verbose_name='手机号')gender = models.IntegerField(choices=GENDERS, verbose_name='性别')pwd = models.CharField(verbose_name='密码', max_length=64)
4.2 视图
from django.http import JsonResponse# Create your views here.
from django.views.decorators.csrf import csrf_exempt
from rest_framework.parsers import JSONParserfrom app04.serializers import UserSerializer
from app04.models import *@csrf_exempt
def user_list(request):if request.method == 'GET':user = User.objects.all()ser = UserSerializer(instance=user, many=True, context={'request': request})return JsonResponse({'status': 200, 'data': ser.data})elif request.method == 'POST':data = JSONParser().parse(request)  # 把前端传过来的json数据转为python里面的数据类型ser = UserSerializer(data=data, context={'request': request})if ser.is_valid():ser.save()return JsonResponse({'status': 201, 'data': ser.data})return JsonResponse({'status': 400, 'data': ser.errors})@csrf_exempt
def user_detail(request, pk):try:user = User.objects.get(pk=pk)except User.DoesNotExist as e:return JsonResponse({'msg': "未获取到pk值", 'status': 404})if request.method == 'GET':ser = UserSerializer(instance=user, context={'request': request})return JsonResponse({'status': 200, 'data': ser.data})elif request.method == 'PUT':data = JSONParser().parse(request)ser = UserSerializer(instance=user, data=data, context={'request': request})if ser.is_valid():ser.save()return JsonResponse({'status': 201, 'data': ser.data})return JsonResponse({'status': 400, 'data': ser.errors})elif request.method == 'PATCH':data = JSONParser().parse(request)ser = UserSerializer(instance=user, data=data, partial=True, context={'request': request})if ser.is_valid():ser.save()return JsonResponse({'status': 201, 'data': ser.data})return JsonResponse({'status': 400, 'data': ser.errors})elif request.method == 'DELETE':user.delete()return JsonResponse({'msg': '删除成功', 'status': 204})
4.3 路由
from django.urls import pathfrom app04 import viewsurlpatterns = [path('users/', views.user_list, name='user-list'),path('users/<int:pk>/', views.user_detail, name='user-detail'),
]
4.4 序列化
from collections import OrderedDictfrom rest_framework import serializers
from .models import User
import reclass ChoiceDisplayField(serializers.Field):"""Custom ChoiceField serializer field."""def __init__(self, choices, **kwargs):"""init."""self._choices = OrderedDict(choices)super(ChoiceDisplayField, self).__init__(**kwargs)# 返回可读性良好的字符串而不是 1,-1 这样的数字def to_representation(self, obj):"""Used while retrieving value for the field."""return self._choices[obj]def to_internal_value(self, data):"""Used while storing value for the field."""for i in self._choices:# 这样无论用户POST上来但是CHOICES的 Key 还是Value 都能被接受if i == data or self._choices[i] == data:return iraise serializers.ValidationError("Acceptable values are {0}.".format(list(self._choices.values())))class UserSerializer(serializers.ModelSerializer):phone = serializers.CharField(max_length=11, min_length=11, required=True)# 可以单独序列化一个模型中不存在的字段,只是不保存数据库而已# 必须要有write_only 不然报错pwd1 = serializers.CharField(max_length=64, write_only=True)# 这样写,支持序列化,不支持反序列化,get_gender_display是一种格式# gender = serializers.CharField(source='get_gender_display')# 方法一# 既支持序列化,又支持反序列化# def to_representation(self, instance):#     representation = super(UserSerializer, self).to_representation(instance)#     representation['gender'] = instance.get_gender_display()#     return representation# 方法二# 支持可读可写GENDERS = ((1, '男'), (2, "女"))gender = ChoiceDisplayField(choices=GENDERS)class Meta:model = Userfields = '__all__'extra_kwargs = {# 写的时候必传,读的时候不需要传'pwd': {'write_only': True}}# 单独验证# 自定义验证函数 validate_属性名 序列化的时候会自动执行这个函数def validate_phone(self, phone):# 验证手机号是否合法if not re.match(r'1[3456789]\d{9}', phone):raise serializers.ValidationError('手机号不合法')# 验证手机号是否被注册过if User.objects.filter(phone=phone).all():raise serializers.ValidationError('手机号已被注册')# 验证通过,一定要有返回值return phone# 组合验证# 验证两次密码是否一致def validate(self, attrs):if attrs.get('pwd1') != attrs.get('pwd'):raise serializers.ValidationError('两次密码输入不正确')# 验证后在pop掉pwd1, 因为数据库中没有pwd1字段if 'pwd1' in attrs:attrs.pop('pwd1')return attrs
4.5 Validators(了解)
  • UniqueValidator:对象是唯一
username = serializers.CharField(required=True, allow_blank=False, label="用户名", max_length=16, min_length=6,validators=[UniqueValidator(queryset=User.objects.all(), message="用户已经存在")],error_messages={"blank": "用户名不允许为空","required": "请输入用户名","max_length": "用户名长度最长为16位","min_length": "用户名长度至少为6位"})
  • UniqueTogetherValidator:联合唯一
class UserFav(models.Model):user = models.ForeignKey(User,verbose_name="用户",on_delete=False)goods = models.ForeignKey(Goods,verbose_name="商品",on_delete=False)add_time = models.DateTimeField(default=datetime.now,verbose_name="用户收藏")class Meta:verbose_name = "用户收藏"verbose_name_plural=verbose_nameunique_together = (('user','goods'),)class UserFavSerializer(serializers.ModelSerializer):user = serializers.HiddenField(default=serializers.CurrentUserDefault())class Meta:model = UserFavfields = ('id','user', 'goods')validators = [UniqueTogetherValidator(queryset=UserFav.objects.all(),fields=('user','goods'),message='您已收藏')]        

五、请求和响应

目前,我们使用的是Django中的request、response、状态码

DRF里面的request是基于Django里面的request进行封装的,DRF中也有response、状态码。

from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework import status

1. 请求

REST里面有个HttpRequest特别相似的对象叫request,主要获取前端传递过来的数据,获取数据的方法就是request.data

2. 响应

REST里面有个HttpResponse特别相似的对象叫Response,主要用来给前端传递数据,传递数据的方法就是Response(data)

3. 状态码

REST里面的状态码比较人性化。每个状态都用意思去表示。比如

HTTP_200_OK = 200  # OK
HTTP_201_CREATED = 201  # 创建成功
HTTP_403_FORBIDDEN = 403  # 权限拒绝
......

4. API视图

API视图主要为了咱们RESTFUL风格的API。主要用来包装request、response、现在api请求方法。

  • 基于函数视图的@api_view装饰器
  • 基于类视图的APIView
4.1 基于函数视图的@api_view装饰器
from app04.serializers import UserSerializer
from app04.models import *
from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework import status@api_view(['GET', 'POST'])
def user_list(request):if request.method == 'GET':user = User.objects.all()ser = UserSerializer(instance=user, many=True, context={'request': request})return Response(ser.data, status=status.HTTP_200_OK)elif request.method == 'POST':ser = UserSerializer(data=request.data, context={'request': request})if ser.is_valid():ser.save()return Response(ser.data, status=status.HTTP_201_CREATED)return Response(ser.errors, status=status.HTTP_400_BAD_REQUEST)
4.2 基于类视图的APIView类
from app04.serializers import UserSerializer
from app04.models import *
from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework import status
from rest_framework.views import APIView
from django.http import Http404class UserDetail(APIView):def get_object(self, pk):try:user = User.objects.get(pk=pk)return userexcept Exception as e:raise Http404()def get(self, request, *args, **kwargs):user = self.get_object(kwargs.get('pk'))ser = UserSerializer(instance=user, context={'request': request})return Response(ser.data, status=status.HTTP_200_OK)def put(self, request, *args, **kwargs):user = self.get_object(kwargs.get('pk'))ser = UserSerializer(instance=user, data=request.data, context={'request': request})if ser.is_valid():ser.save()return Response(ser.data, status=status.HTTP_201_CREATED)return Response(ser.errors, status=status.HTTP_400_BAD_REQUEST)def patch(self, request, *args, **kwargs):user = self.get_object(kwargs.get('pk'))ser = UserSerializer(instance=user, data=request.data, context={'request': request}, partial=True)if ser.is_valid():ser.save()return Response(ser.data, status=status.HTTP_201_CREATED)return Response(ser.errors, status=status.HTTP_400_BAD_REQUEST)def delete(self, request, *args, **kwargs):user = self.get_object(kwargs.get('pk'))user.delete()return Response(status=status.HTTP_204_NO_CONTENT)

5. 路由

from django.urls import pathfrom app04 import viewsurlpatterns = [path('users/', views.user_list, name='user-list'),# path('users/<int:pk>/', views.user_detail, name='user-detail'),path('users/<int:pk>/', views.UserDetail.as_view(), name='user-detail'),
]

六、类视图

使用类视图最大的好处就是可以创建复用的行为。

1. mixins

我们常用的操作比如创建、更新、删除、查找。REST框架已经帮我们写好了,写的代码就在mixins这个类里面。

所以只有我们自己的视图直接继承这个类,就完全可以拥有上面的所有功能。

from rest_framework import mixins
from rest_framework import genericsclass UserList(mixins.ListModelMixin, mixins.CreateModelMixin, generics.GenericAPIView):queryset = User.objects.all()serializer_class = UserSerializerdef get(self, request, *args, **kwargs):return self.list(request, *args, **kwargs)def post(self, request, *args, **kwargs):return self.create(request, *args, **kwargs)
# RetrieveModelMixin获取一个模型实例
class UserDetail(mixins.RetrieveModelMixin, mixins.UpdateModelMixin, mixins.DestroyModelMixin, generics.GenericAPIView):queryset = User.objects.all()serializer_class = UserSerializerdef get(self, request, *args, **kwargs):return self.retrieve(request, *args, **kwargs)def put(self, request, *args, **kwargs):return self.update(request, *args, **kwargs)def patch(self, request, *args, **kwargs):kwargs['partial'] = Truereturn self.partial_update(request, *args, **kwargs)def delete(self, request, *args, **kwargs):return self.destroy(request, *args, **kwargs)

注意:路由关键字的参数要是pk

2. 路由

from django.urls import pathfrom app04 import viewsurlpatterns = [# path('users/', views.user_list, name='user-list'),path('users/', views.UserList.as_view(), name='user-list'),# path('users/<int:pk>/', views.user_detail, name='user-detail'),path('users/<int:pk>/', views.UserDetail.as_view(), name='user-detail'),
]

3. 使用通用的基于类的视图

我们可以可以看到我的代码量已经减少了很多,但是我们还可以继续优化。REST把我们常见的操作继续封装了起来,封装 起来的代码就在ListCreateAPIViewRetrieveUpdateDestroyAPIView

class UserList(generics.ListCreateAPIView):queryset = User.objects.all()serializer_class = UserSerializerclass UserDetail(generics.RetrieveUpdateDestroyAPIView):queryset = User.objects.all()serializer_class = UserSerializer

七、认证和权限

现在我们写的接口,会发现一个问题,就是任何人都可以创建数据,都可以修改数据。这样肯定是不行的,我们希望只有数据的创建者才能有权限修改数据。如果不是,只能有读取权限。

1. 创建用户

在终端中执行命令:python manage.py createsuperuser

2. 快速体验

2.1 模型类
from django.db import models# Create your models here.
class Game(models.Model):name = models.CharField(verbose_name='游戏名字', max_length=10)desc = models.CharField(verbose_name='描述', max_length=20)user = models.ForeignKey('auth.User', on_delete=models.CASCADE)
2.2 视图
from django.shortcuts import render
from rest_framework import generics
from .models import Game
from .serializers import GameSerializer
from rest_framework.authentication import SessionAuthentication, BasicAuthentication# Create your views here.class GameList(generics.ListCreateAPIView):queryset = Game.objects.all()serializer_class = GameSerializer# 重写perform_create 创建的时候提供当前登录的用户def perform_create(self, serializer):serializer.save(user=self.request.user)# 到底谁给request.user赋值的 DRF中的认证类class GameDetail(generics.RetrieveUpdateDestroyAPIView):queryset = Game.objects.all()serializer_class = GameSerializer
2.3 序列化类
from rest_framework import serializers
from .models import Gameclass GameSerializer(serializers.ModelSerializer):class Meta:model = Gamefields = '__all__'extra_kwargs = {"user": {"read_only": True}}
2.4 路由
from django.urls import pathfrom app05 import viewsurlpatterns = [path('games/', views.GameList.as_view(), name='game-list'),path('games/<int:pk>/', views.GameDetail.as_view(), name='game-detail'),
]
2.5 测试

http://127.0.0.1:8000/api/app05/games/

首先在Body–>raw中,传入json数据,如下:

{"name": "和平精英","desc": "射击"
}

其次在Authorization中,Type选择Basic Auth,输入上面创建的用户即可。

2.6 总结

DRF默认认证是SessionAuthentication、BasicAuthentication

目前采用BasicAuthentication,通过浏览器传递账号密码,base64加密 ,认证成功后,返回一个元组
元组的第一个元素(用户对象),赋值给 request

我们重写 perform_create 就可以从 request 拿到 user

3. 权限

刚才我们创建两个用户,现在我通过 nice1 去更新我们 nice 创建的数据,发现是可以更新的,但是这个并不是我们想要的。

现在我们有两种情况,第一,不认证也能修改成功;第二,其他人认证后也能修改成功。

3.1 添加权限

创建一个permissions.py,写如下代码:

from rest_framework.permissions import BasePermission, SAFE_METHODSclass IsOwnOrReadOnly(BasePermission):# 重写has_object_permissiondef has_object_permission(self, request, view, obj):if request.method in SAFE_METHODS:return True# obj指模型对象return obj.user == request.user

上面代码的意思代表判断当前的数据创建用户是否跟当前登录用户是否一个人。并在更新视图上加上该权限

from django.shortcuts import render
from rest_framework import generics
from .models import Game
from .serializers import GameSerializer
from rest_framework.authentication import SessionAuthentication, BasicAuthentication
from rest_framework import permissions
from .permissions import IsOwnOrReadOnly# Create your views here.class GameList(generics.ListCreateAPIView):queryset = Game.objects.all()serializer_class = GameSerializer# 重写perform_create 创建的时候提供当前登录的用户def perform_create(self, serializer):serializer.save(user=self.request.user)# 到底谁给request.user赋值的 DRF中的认证类class GameDetail(generics.RetrieveUpdateDestroyAPIView):queryset = Game.objects.all()serializer_class = GameSerializer# 第一个权限解决不认证也能修改成功的情况# 第二个自定义认证权限解决其他人认证也能修改成功的情况permission_classes = [permissions.IsAuthenticatedOrReadOnly, IsOwnOrReadOnly]
3.2 测试

测试方法和上面一样,没有权限如下:

{"detail": "You do not have permission to perform this action."
}

4. 认证

  • BasicAuthentication:此身份验证方案使用 HTTP基本身份验证,根据用户的用户名和密码进行签名。基本身份验证通常仅适用于测试。
  • TokenAuthentication:此身份验证方案使用基于令牌的简单HTTP身份验证方案。令牌认证适用于客户端 - 服务器设置,例如本机桌面和移动客户端。
  • SessionAuthentication:此身份验证方案使用 Django 的默认会话后端进行身份验证。会话身份验证适用于与您的网站在同一会话上下文中运行的AJAX客户端。
  • RemoteUserAuthentication:此身份验证方案允许您将身份验证委派给Web服务器,该服务器设置REMOTE_USER 环境变量。
4.1 BasicAuthentication

Http Basic 是一种比较简单的身份认证方式。 在 Http header 中添加键值对 Authorization: Basic xxx (xxx 是 username:passowrd base64 值)。而Base64 的解码是非常方便的,如果不使用 Https ,相当于是帐号密码直接暴露在请求中。

GET /auth/basic/ HTTP/1.1
Host: xxxxx
Authorization: Basic em1rOjEyMzQ1Ng==
4.1.1 路由
from django.urls import pathfrom app06 import viewsurlpatterns = [path('carts/', views.CartView.as_view(), name='cart-list'),
]
4.1.2 视图
from django.shortcuts import render
from rest_framework.views import APIView
from django.http import JsonResponse
from rest_framework.authentication import BasicAuthentication
from rest_framework.permissions import IsAuthenticated# Create your views here.class CartView(APIView):# 基于什么登录认证的# 登录有很多方式,这里基于浏览器账号密码登录的authentication_classes = [BasicAuthentication]# 只有登录才能访问permission_classes = [IsAuthenticated]def get(self, request, *args, **kwargs):ctx = {"code": 1,"msg": "ok","data": {"goods": [{"name": "苹果","price": 12},{"name": "橘子","price": 10}]}}return JsonResponse(data=ctx, json_dumps_params={'ensure_ascii': False})
4.1.3 小结

DRF默认认证是SessionAuthentication、BasicAuthentication

支持多个认证,取认证成功的那个认证方法

4.2 TokenAuthentication
4.2.1 配置
# settings.py
INSTALLED_APPS = (...'rest_framework.authtoken'
)
# 配置完上面,需要迁移,python manage.py migrate# 全局配置
REST_FRAMEWORK = {'DEFAULT_AUTHENTICATION_CLASSES': ('rest_framework.authentication.BasicAuthentication','rest_framework.authentication.SessionAuthentication','rest_framework.authentication.TokenAuthentication',),
}
# 如果有些视图不需要验证,可以将该类中认证设置为空
# authentication_classes = []# 局部配置
from rest_framework.authentication import BasicAuthentication, TokenAuthentication, SessionAuthentication
class XXX(APIView):authentication_classes = [BasicAuthentication, TokenAuthentication, SessionAuthentication]
4.2.2 路由

放在根路由和子路由都可以,我写在了根路由。

from rest_framework.authtoken.views import obtain_auth_token
urlpatterns = [# api-token-auth是随便起的path('api-token-auth/', obtain_auth_token),
]

这个路由的作用是生成 token

  • 测试生成 token 接口

post 请求,http://10.36.172.81:8000/api-token-auth/

在 body 的 x-www-form-urlencoded 中设置 username 和 password,测试结果如下:

{"token": "596b0ffd0d02df6e745b4e1a6d08bec999bd95ed"
}
4.2.3 视图
from django.shortcuts import render
from rest_framework.views import APIView
from django.http import JsonResponse
from rest_framework.authentication import BasicAuthentication, TokenAuthentication
from rest_framework.permissions import IsAuthenticated# Create your views here.class CartView(APIView):# 基于什么登录认证的# 登录有很多方式,这里基于浏览器账号密码登录的authentication_classes = [BasicAuthentication, TokenAuthentication]# 只有登录才能访问permission_classes = [IsAuthenticated]def get(self, request, *args, **kwargs):ctx = {"code": 1,"msg": "ok","data": {"goods": [{"name": "苹果","price": 12},{"name": "橘子","price": 10}]}}return JsonResponse(data=ctx, json_dumps_params={'ensure_ascii': False})
4.2.4 测试

封装到请求头中,在 Headers 中书写 key 和 value ,其中 token 是 4.2.2 中生成的。

Authorization: Token 596b0ffd0d02df6e745b4e1a6d08bec999bd95ed

认证成功后,返回数据,失败返回如下:

{"detail": "Invalid token."
}

token值在分布式系统中会有问题产生,并且没有过期时间,一旦被窃取,任何人都可以使用。

4.3 SessionAuthentication
4.3.1 视图
from django.shortcuts import render
from rest_framework.views import APIView
from django.http import JsonResponse
from rest_framework.authentication import BasicAuthentication, TokenAuthentication, SessionAuthentication
from rest_framework.permissions import IsAuthenticated# Create your views here.class CartView(APIView):# 基于什么登录认证的# 登录有很多方式,这里基于浏览器账号密码登录的authentication_classes = [BasicAuthentication, TokenAuthentication, SessionAuthentication]# 只有登录才能访问permission_classes = [IsAuthenticated]def get(self, request, *args, **kwargs):ctx = {"code": 1,"msg": "ok","data": {"goods": [{"name": "苹果","price": 12},{"name": "橘子","price": 10}]}}return JsonResponse(data=ctx, json_dumps_params={'ensure_ascii': False})
4.3.2 测试

get请求,在 Headers 中书写 key 和 value ,格式如下:

Cookie: csrftoken=SEmCnnmHVE0XZSFhBLPQD8HjLf43eCZdLU0aVvEjtlaf0hRjLvZmDmIwM9sl62lq; sessionid=h4jp85en5zspsthqffx66x1noc2y9k6r
4.4 自定义认证
4.4.1 模型类
from django.db import models# Create your models here.
class User(models.Model):username = models.CharField(max_length=32, unique=True)password = models.CharField(max_length=64)class UserToken(models.Model):user = models.OneToOneField('User', models.CASCADE)token = models.CharField(max_length=64)
4.4.2 视图
import hashlib
import timefrom rest_framework.views import APIView
from django.http import JsonResponse
from rest_framework.authentication import BasicAuthentication
from rest_framework import exceptions
from .permissions import MyPermission# Create your views here.
from app06.models import User, UserTokendef get_md5(user):ctime = str(time.time())m = hashlib.md5(bytes(user, encoding='utf-8'))m.update(bytes(ctime, encoding='utf-8'))return m.hexdigest()class LoginView(APIView):def post(self, request, *args, **kwargs):ret = {'code': 1, 'msg': None, 'data': {}}# user = request._request.POST.get('username')# pwd = request._request.POST.get('password')# 等价于下面user = request.POST.get('username')pwd = request.POST.get('password')obj = User.objects.filter(username=user, password=pwd).first()if not obj:ret['code'] = -1ret['msg'] = '用户名或密码错误'token = get_md5(user)UserToken.objects.update_or_create(user=obj, defaults={'token': token})ret['token'] = tokenreturn JsonResponse(ret)class MyAuthentication(BasicAuthentication):# 必须重写这个方法def authenticate(self, request):# header key必须大写,前缀必须是"HTTP",后面如果连接符是横线"-",要改成下划线"_"# 例如你的header的key为api_auth,那在Django中应该使用request.META.get("HTTP_API_AUTH")来获取请求头的数据token = request.META.get('HTTP_TOKEN')obj = UserToken.objects.filter(token=token).first()if not obj:raise exceptions.AuthenticationFailed('认证失败')else:# 必须返回一个元组return (obj.user, obj)class CartView(APIView):# 自定义认证authentication_classes = [MyAuthentication]# 自定义权限permission_classes = [MyPermission]def get(self, request, *args, **kwargs):ctx = {"code": 1,"msg": "ok","data": {"goods": [{"name": "苹果","price": 12},{"name": "橘子","price": 10}]}}return JsonResponse(data=ctx, json_dumps_params={'ensure_ascii': False})
4.4.3 自定义权限

创建一个 permissions.py 文件

from rest_framework.permissions import BasePermissionclass MyPermission(BasePermission):def has_permission(self, request, view):if not request.user:return Falsereturn True
4.4.4 路由
from django.urls import pathfrom app06 import viewsurlpatterns = [path('carts/', views.CartView.as_view(), name='cart-list'),path('login/', views.LoginView.as_view(), name='login'),
]
4.4.5 测试

首先,生成 token 。post 请求,http://127.0.0.1:8000/api/app06/login

在 Body 的 x-www-form-urlencoded 输入 username 和 password(必须在我们自己创建的模型库中存在)

登录认证成功后,产生 token ,携带 token 访问 http://127.0.0.1:8000/api/app06/carts/ ,get 请求,并在 Headers 中输入 token 和 上面生成的 token 值。

4.5 JWT验证

使用 django-rest-framework 开发 api 并使用 json web token 进行身份验证,使用 django-rest-framework-jwt 这个库来帮助我们简单的使用 jwt 进行身份验证。token 不会保存在服务器,保存在了客户端。

4.5.1 安装
pip install djangorestframework-jwt
4.5.2 注册
INSTALLED_APPS = [......'rest_framework','rest_framework.authtoken',
]
4.5.3 配置
# 全局配置
REST_FRAMEWORK = {'DEFAULT_AUTHENTICATION_CLASSES': ('rest_framework.authentication.BasicAuthentication','rest_framework.authentication.SessionAuthentication','rest_framework.authentication.TokenAuthentication',# 配置验证方式为Token验证'rest_framework_jwt.authentication.JSONWebTokenAuthentication'),'DEFAULT_PERMISSION_CLASSES': ('rest_framework.permissions.IsAuthenticated',)
}import datetime
JWT_AUTH = {'JWT_EXPIRATION_DELTA': datetime.timedelta(days=7),  # Token 过期时间为一周'JWT_AUTH_HEADER_PREFIX': 'JWT',  # Token的头为: JWT adashkjdhaskjhd21312312'JWT_ALLOW_REFRESH': False,'JWT_RESPONSE_PAYLOAD_HANDLER': 'app06.utils.jwt_response_payload_handler'
}
4.5.4 路由
from rest_framework_jwt.views import obtain_jwt_token
urlpatterns = [# path('admin/', admin.site.urls),# path('api/', include(router.urls)),# path('api/app02/', include('app02.urls')),# path('api/app03/', include(('app03.urls', 'app03'), namespace='app03')),# path('api/app04/', include(('app04.urls', 'app04'), namespace='app04')),# path('api/app05/', include(('app05.urls', 'app05'), namespace='app05')),# path('api/app06/', include(('app06.urls', 'app06'), namespace='app06')),# path('api-token-auth/', obtain_auth_token),path('api-token-auth/', obtain_jwt_token),]

post 请求,http://127.0.0.1:8000/api-token-auth/

在 Body 的x-www-form-urlencoded 或 form-data 中输入 username 和 password,结果如下:

{"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6Im5pY2UiLCJleHAiOjE1OTgyODI1MDcsImVtYWlsIjoibmljZUBxcS5jb20ifQ.zFVFf0maDoIT76fKkgAum8cpqTUN7JQmrlrKkdQmX5Y"
}

默认的返回值仅有 token,通过修改该视图的返回值可以完成我们的需求。在应用中新建一个 utils.py 文件,内容如下:

def jwt_response_payload_handler(token, user=None, request=None):# 除了返回token外,还返回 id 和 usernamereturn {'token': token,'id': user.id,'username': user.username,}

然后在 settings.py 中配置

import datetime
JWT_AUTH = {'JWT_EXPIRATION_DELTA': datetime.timedelta(days=7),  # Token 过期时间为一周'JWT_AUTH_HEADER_PREFIX': 'JWT',  # Token的头为: JWT adashkjdhaskjhd21312312'JWT_ALLOW_REFRESH': False,'JWT_RESPONSE_PAYLOAD_HANDLER': 'app06.utils.jwt_response_payload_handler'
}

测试结果为:

{"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6Im5pY2UiLCJleHAiOjE1OTgyODI1MzQsImVtYWlsIjoibmljZUBxcS5jb20ifQ.HjEJAASvnIxjOAhKC-pE72pY1YKswtbPGX93djKPtzs","id": 1,"username": "nice"
}

我们可以将 JWT 保存在 cookie 中,也可以保存在浏览器的本地存储里,我们保存在浏览器本地存储中,下次使用的时候带上即可。

4.5.5 视图
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
class CartView(APIView):# 基于jwt认证authentication_classes = [JSONWebTokenAuthentication]# 权限 只有登录才能访问permission_classes = [IsAuthenticated]def get(self, request, *args, **kwargs):ctx = {"code": 1,"msg": "ok","data": {"goods": [{"name": "苹果","price": 12},{"name": "橘子","price": 10}]}}return JsonResponse(data=ctx, json_dumps_params={'ensure_ascii': False})
4.5.6 测试

get 请求,http://127.0.0.1:8000/api/app06/carts/

在 Headers 中,输入 key 和 value,其中 key 是 Authorization,value 是 JWT + 两个空格 + 生成的 token 值。

4.5.7 JWT权限验证

和 3.1 中实现效果一样

from rest_framework_jwt.authentication import jwt_decode_handler# 判断当前数据的创建者是否与当前登录的用户一样
class IsOwnOrReadOnly(BasePermission):def has_object_permission(self, request, view, obj):# if request.method in SAFE_METHODS:#     return True# token = request.META.get('HTTP_AUTHORIZATION')[5:]token = request.META.get('HTTP_AUTHORIZATION').split()[-1]  # token解析出来token_user = jwt_decode_handler(token)if token_user:# obj.user.id数据创建者的idreturn obj.user.id == token_user.get('user_id')  # 证明 当前jwt验证的用户就是数据的创建者return False
4.6 JWT优缺点

优点

  • 无状态(保存在客户端)
  • 避免csrf
  • 适合移动端

缺点

  • 注销登录后Token时效问题(可以把token放到redis中,当我们删除用户时,删除token)

八、频率

开放平台的API接口调用需要限制其频率,以节约服务器资源和避免恶意的频繁调用。

3/min{"127.0.0.1":["12.29.50","12.29.40","12.29.20"]}12.30.10   访问记录大于3 访问不了12.30.21   能访问
"127.0.0.1":["12.30.21","12.29.50","12.29.40"]}
from rest_framework.throttling import SimpleRateThrottleallow_request 必须实现的方法AnonRateThrottle类:匿名用户 UserRateThrottle类:已认证用户ScopedRateThrottle类:每个用户对视图的访问次数

1. 内置频率类

在应用下新建一个throttlings.py 文件。如下代码:

from rest_framework.throttling import SimpleRateThrottle# {"127.0.0.1":["12.29.50","12.29.40","12.29.20"]}
# key会缓存到django的缓存中,key相当于上面的127.0.0.1
class VisitThrottling(SimpleRateThrottle):scope = '未认证用户'def get_cache_key(self, request, view):return self.get_ident(request)  # 使用ip当作keyclass UserThrottling(SimpleRateThrottle):scope = '已认证用户'def get_cache_key(self, request, view):return request.user  # 当前登录的用户当做key

2. 配置

# 全局配置
REST_FRAMEWORK = {'DEFAULT_AUTHENTICATION_CLASSES': ('rest_framework.authentication.BasicAuthentication','rest_framework.authentication.SessionAuthentication','rest_framework.authentication.TokenAuthentication',# 配置验证方式为Token验证'rest_framework_jwt.authentication.JSONWebTokenAuthentication'),'DEFAULT_PERMISSION_CLASSES': ('rest_framework.permissions.IsAuthenticated',),# 节流配置下面这两项'DEFAULT_THROTTLE_CLASSES': ['app06.throttlings.UserThrottling', ],'DEFAULT_THROTTLE_RATES': {'未认证用户': '3/m','已认证用户': '10/m',},
}

3. 视图

视图中配置局部节流

class CartView(APIView):# 基于jwt认证authentication_classes = [JSONWebTokenAuthentication]# 权限 只有登录才能访问permission_classes = [IsAuthenticated]# 节流 局部throttle_classes = [VisitThrottling]def get(self, request, *args, **kwargs):ctx = {"code": 1,"msg": "ok","data": {"goods": [{"name": "苹果","price": 12},{"name": "橘子","price": 10}]}}return JsonResponse(data=ctx, json_dumps_params={'ensure_ascii': False})

九、版本控制

API 版本控制允许我们在不同的客户端之间更改行为(同一个接口的不同版本会返回不同的数据)。 DRF提供了许多不同的版本控制方案。

可能会有一些客户端因为某些原因不再维护了,但是我们后端的接口还要不断的更新迭代,这个时候通过版本控制返回不同的内容就是一种不错的解决方案。

1. 五种控制方案

from rest_framework.versioning import BaseVersioning# 在请求头中
class AcceptHeaderVersioning(BaseVersioning):"""GET /something/ HTTP/1.1Host: example.comAccept: application/json; version=1.0"""pass# 在URL中
class URLPathVersioning(BaseVersioning):"""urlpatterns = [url(r'^(?P<version>[v1|v2]+)/users/$', users_list, name='users-list'),url(r'^(?P<version>[v1|v2]+)/users/(?P<pk>[0-9]+)/$', users_detail, name='users-detail')]GET /1.0/something/ HTTP/1.1Host: example.comAccept: application/json"""pass# 使用别名namespace
class NamespaceVersioning(BaseVersioning):"""# users/urls.pyurlpatterns = [url(r'^/users/$', users_list, name='users-list'),url(r'^/users/(?P<pk>[0-9]+)/$', users_detail, name='users-detail')]# urls.pyurlpatterns = [url(r'^v1/', include('users.urls', namespace='v1')),url(r'^v2/', include('users.urls', namespace='v2'))]GET /1.0/something/ HTTP/1.1Host: example.comAccept: application/json"""pass# 通过主机域
class HostNameVersioning(BaseVersioning):"""GET /something/ HTTP/1.1Host: v1.example.comAccept: application/json"""pass# 通过URL查询参数
class QueryParameterVersioning(BaseVersioning):"""GET /something/?version=0.1 HTTP/1.1Host: example.comAccept: application/json"""pass

2. 视图

class VersionView(APIView):def get(self, request, *args, **kwargs):# print(request.version)  # v1if request.version == 'v1':ctx = {'code': 1, 'msg': 'ok', 'data': {}}return JsonResponse(ctx)else:ctx = {'code': 2, 'msg': 'ok', 'data': {}}return JsonResponse(ctx)

3. 路由

子路由

urlpatterns = [path('carts/', views.CartView.as_view(), name='cart-list'),path('login/', views.LoginView.as_view(), name='login'),path('<str:version>/version', views.VersionView.as_view(), name='version'),
]

根路由

urlpatterns = [path('api/app06/', include(('app06.urls', 'app06'), namespace='app06'))
]

4. 配置

# 全局配置
REST_FRAMEWORK = {# 认证'DEFAULT_AUTHENTICATION_CLASSES': ('rest_framework.authentication.BasicAuthentication','rest_framework.authentication.SessionAuthentication','rest_framework.authentication.TokenAuthentication',# 配置验证方式为Token验证'rest_framework_jwt.authentication.JSONWebTokenAuthentication'),# 权限'DEFAULT_PERMISSION_CLASSES': ('rest_framework.permissions.IsAuthenticated',),# 节流'DEFAULT_THROTTLE_CLASSES': ['app06.throttlings.UserThrottling', ],'DEFAULT_THROTTLE_RATES': {'未认证用户': '3/m','已认证用户': '10/m',},# 版本'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.URLPathVersioning','DEFAULT_VERSION': 'v1',  # 默认的版本'ALLOWED_VERSIONS': ['v1', 'v2'],  # 有效的版本'VERSION_PARAM': 'version',  # 版本的参数名与路由中一致
}
#局部配置
versioning_class = "注意不是列表"

5. 通用的基于类的视图中的用法

5.1 模型
from django.db import models# Create your models here.
class Game(models.Model):name = models.CharField(max_length=10)status = models.IntegerField()
5.2 序列化
from rest_framework import serializers
from .models import Gameclass GameSerializer(serializers.ModelSerializer):class Meta:model = Gamefields = '__all__'
5.3 视图
from django.shortcuts import render
from rest_framework import generics
from .models import Game
from .serializers import GameSerializer# Create your views here.
class GameList(generics.ListCreateAPIView):serializer_class = GameSerializerdef get_queryset(self):if self.request.version == 'v1':queryset = Game.objects.filter(status=1).all()else:queryset = Game.objects.filter(status=0).all()return queryset# def get_serializer_class(self):#     # 序列化也支持重写,返回不同的序列化类#     passclass GameDetail(generics.RetrieveUpdateDestroyAPIView):queryset = Game.objects.all()serializer_class = GameSerializer
5.4 路由

子路由

from django.urls import pathfrom app07 import viewsurlpatterns = [path('games/', views.GameList.as_view(), name='game-list'),path('games/<int:pk>', views.GameDetail.as_view(), name='game-detail'),
]

根路由

urlpatterns = [path('api/app07/<str:version>/', include(('app07.urls', 'app07'), namespace='app07'))
]
5.5 测试

get 请求

http://127.0.0.1:8000/api/app07/v1/games/

http://127.0.0.1:8000/api/app07/v2/games/

十、分页

当对于数据量大的时候,我们就需要采用分页操作。

1. 分页方式

  • 普通分页,看第n页,每页显示m条数据;
  • 切割分页,在n个位置,向后查看m条数据;
  • 加密分页,这与普通分页方式相似,不过对url中的请求页码进行加密。

2. 普通分页

新建一个 pagenumberpagination.py 文件

from rest_framework.pagination import PageNumberPaginationclass MyPageNumberPagination(PageNumberPagination):page_size = 2max_page_size = 5page_size_query_param = 'size'page_query_param = 'page'

page_query_param:表示url中的页码参数
page_size_query_param:表示url中每页数量参数
page_size:表示每页的默认显示数量
max_page_size:表示每页最大显示数量,做限制使用,避免突然大量的查询数据,数据库崩溃

3. 切割分页

from rest_framework.pagination import PageNumberPagination, LimitOffsetPaginationclass MyPageNumberPagination(LimitOffsetPagination):default_limit = 2limit_query_param = 'limit'offset_query_param = 'offset'max_limit = 5

default_limit:表示默认每页显示几条数据
limit_query_param:表示url中本页需要显示数量参数
offset_query_param:表示从数据库中的第几条数据开始显示参数
max_limit:表示每页最大显示数量,做限制使用,避免突然大量的查询数据,数据库崩溃

4. 加密分页

from rest_framework.pagination import PageNumberPagination, LimitOffsetPagination, CursorPaginationclass MyPageNumberPagination(CursorPagination):cursor_query_param = 'cursor'page_size = 1ordering = 'id'page_size_query_param = 'size'max_page_size = 1

cursor_query_param:表示url中页码的参数
page_size_query_param:表示每页显示数据量的参数
max_page_size:表示每页最大显示数量,做限制使用,避免突然大量的查询数据,数据库崩溃
ordering:表示返回数据的排序方式

5. 配置

# 全局配置
REST_FRAMEWORK = {# 认证'DEFAULT_AUTHENTICATION_CLASSES': ('rest_framework.authentication.BasicAuthentication','rest_framework.authentication.SessionAuthentication','rest_framework.authentication.TokenAuthentication',# 配置验证方式为Token验证'rest_framework_jwt.authentication.JSONWebTokenAuthentication'),# 权限'DEFAULT_PERMISSION_CLASSES': ('rest_framework.permissions.IsAuthenticated',),# 节流'DEFAULT_THROTTLE_CLASSES': ['app06.throttlings.UserThrottling', ],'DEFAULT_THROTTLE_RATES': {'未认证用户': '3/m','已认证用户': '10/m',},# 版本'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.URLPathVersioning','DEFAULT_VERSION': 'v1',  # 默认的版本'ALLOWED_VERSIONS': ['v1', 'v2'],  # 有效的版本'VERSION_PARAM': 'version',  # 版本的参数名与URL conf中一致# 分页'DEFAULT_PAGINATION_CLASS': 'app07.pagenumberpagination.MyPageNumberPagination'
}

人人都能看懂的Django REST framework相关推荐

  1. 人人都能看懂LSTM

    这是在看了台大李宏毅教授的深度学习视频之后的一点总结和感想.看完介绍的第一部分RNN尤其LSTM的介绍之后,整个人醍醐灌顶.本篇博客就是对视频的一些记录加上了一些个人的思考. 0. 从RNN说起 循环 ...

  2. 人人都能看懂的LSTMGRU

    看过的讲的最简单明了的: LSTM:人人都能看懂的LSTM GRU:人人都能看懂的GRU 自己对LSTM的理解与代码解释:https://blog.csdn.net/Strive_For_Future ...

  3. 人人都能看懂的Spring源码解析,Spring如何解决循环依赖

    人人都能看懂的Spring源码解析,Spring如何解决循环依赖 原理解析 什么是循环依赖 循环依赖会有什么问题? 如何解决循环依赖 问题的根本原因 如何解决 为什么需要三级缓存? Spring的三级 ...

  4. 人人都能看懂的Spring底层原理,看完绝对不会懵逼

    人人都能看懂的Spring原理,绝对不会懵逼 为什么要使用Spring? Spring的核心组件 Spring是如何实现IOC和DI的? 定义了BeanDefinition 扫描加载BeanDefin ...

  5. 人人都能看懂的EM算法推导

    作者丨August@知乎(已授权) 来源丨https://zhuanlan.zhihu.com/p/36331115 编辑丨极市平台 估计有很多入门机器学习的同学在看到EM算法的时候会有种种疑惑:EM ...

  6. em算法 实例 正态分布_人人都能看懂的EM算法推导

    ↑ 点击蓝字 关注极市平台作者丨August@知乎(已授权)来源丨https://zhuanlan.zhihu.com/p/36331115编辑丨极市平台 极市导读 EM算法到底是什么,公式推导怎么去 ...

  7. 人人都能看懂的 Python 装饰器入门教程

    大家好,我是萱萱! 之前的文章中提到,很多人认为理解了装饰器的概念和用法后,会觉得自己的 Python 水平有一个明显的提高. 但很多教程在一上来就会给出装饰器的定义以及基本用法,例如你一定会在很多文 ...

  8. 人人都能看懂的机器学习!3个案例详解聚类、回归、分类算法

    导读:机器是怎样学习的,都学到了什么?人类又是怎样教会机器学习的?本文通过案例给你讲清楚各类算法的原理和应用. 机器学习,一言以蔽之就是人类定义一定的计算机算法,让计算机根据输入的样本和一些人类的干预 ...

  9. 人人都能看懂的 Python 装饰器入门教程!

    大家好,我是菜鸟哥! 之前的文章中提到,很多人认为理解了装饰器 的概念和用法后,会觉得自己的 Python 水平有一个明显的提高. 但很多教程在一上来就会给出装饰器的定义以及基本用法,例如你一定会在很 ...

最新文章

  1. 手把手教你写一个Java的orm框架(4)
  2. DbLookUpCombobox的使用方法
  3. linux 产生三位数的随机数
  4. spring-注解---IOC(3)
  5. WebKit编译小结
  6. 隐藏桌面图标软件——CoverDesk for Mac免激活版
  7. bat之长ping保存在文本
  8. 安卓TextView完美展示html格式代码
  9. 教育平台的线上课程 智能推荐策略
  10. 计算机高特效吃鸡游戏主机配置单,畅玩主流游戏吃鸡LOL组装电脑配置清单
  11. iOS 多语言本地化 完美解决方案【自动+手动】
  12. 【设计模式】:单例设计模式深究
  13. 用css伪元素制作箭头图标
  14. web前端基础——超链接(dw笔记版)
  15. 大脑构造图与功能解析_大脑结构与功能
  16. ElasticSearch使用学习
  17. 为网站配置免费的HTTPS证书 3-4
  18. Stata实验——关于我国GDP增长率和CONS增长率
  19. I-NUIST南京信息工程大学Android设备校园网自动登录
  20. anaconda配置清华大学开源软件镜像

热门文章

  1. 30岁的人生从减法开始
  2. SaltStack介绍
  3. python学习-通过md5/sha1/sha256/base64进行加解密
  4. java json injection_JSON Injection解决方法
  5. 乒乓球训练机_首款乒乓球训练机器人来了 马龙继科给你当陪练
  6. 一探究竟:MyBatis的mapper查询接口返回的list会不会是null?
  7. 搜索+图论 你我都能看懂
  8. 正点原子 DAP下载器简单使用
  9. 高并发场景下backlog详解
  10. 计算机组装部件推荐,2018最新组装电脑高配置清单推荐