人人都能看懂的Django REST framework

Posted 南枝向暖北枝寒MA

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了人人都能看懂的Django REST framework相关的知识,希望对你有一定的参考价值。

文章目录

一、前后端分离

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的主要内容,如果未能解决你的问题,请参考以下文章

人人都能看懂的Django REST framework

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

人人都能看懂的LSTM

收藏 | 人人都能看懂的 LSTM

人人都能看懂的LSTM

人人都能看懂的云计算知识科普 !