人人都能看懂的Django REST framework
Posted 南枝向暖北枝寒MA
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了人人都能看懂的Django REST framework相关的知识,希望对你有一定的参考价值。
文章目录
- 一、前后端分离
- 二、RESTful API
- 三、快速入手
- 四、序列化
- 1. 普通序列化
- 2. 模型序列化ModelSerializer
- 3. 模型序列化(高级)
- 4. 序列化验证
- 五、请求和响应
- 六、类视图
- 七、认证和权限
- 八、频率
- 九、版本控制
- 十、分页
一、前后端分离
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/apples
http://www.example.com/app/v1.1/apples
http://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 = Article
fields = ('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 JSONParser
from 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 path
from app03 import views
urlpatterns = [
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.name
class 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 = Article
fields = ('id', 'title', 'vnum', 'content', 'category')
class CategorySerializer(serializers.ModelSerializer):
class Meta:
model = Category
fields = '__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.以上是关于人人都能看懂的Django REST framework的主要内容,如果未能解决你的问题,请参考以下文章