从小白到高手---api接口和drf(Django Rest_Framework)使用超详解

1.api接口

为了在团队内部形成共识、防止个人习惯差异引起的混乱,我们需要找到一种大家都觉得很好的接口实现规范,而且这种规范能够让后端写的接口,用途一目了然,减少双方之间的合作成本。

目前市面上大部分公司开发人员使用的接口服务架构主要有:restful、rpc。

rpc: 翻译成中文:远程过程调用[远程服务调用].

http://www.lufei.com/api

post请求

action=get_all_student¶ms=301&sex=1

接口多了,对应函数名和参数就多了,前端在请求api接口时,就会比较难找.容易出现重复的接口

restful: 翻译成中文: 资源状态转换.

把后端所有的数据/文件都看成资源.

那么接口请求数据,本质上来说就是对资源的操作了.

web项目中操作资源,无非就是增删查改.所以要求在地址栏中声明要操作的资源是什么,然后通过http请求动词来说明对资源进行哪一种操作.

POST http://www.lufei.com/api/students/ 添加学生数据

GET http://www.lufei.com/api/students/ 获取所有学生

DELETE http://www.lufei.com/api/students/ 删除1个学生

2.RESTful API规范

1. 域名

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

https://www.jd.com
https://api.example.com

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

https://example.org/api/

2. 版本(Versioning)

应该将API的版本号放入URL。

http://www.example.com/app/1.0/foo

http://www.example.com/app/1.1/foo

http://www.example.com/app/2.0/foo

另一种做法是,将版本号放在HTTP头信息中,但不如放入URL方便和直观。Github就采用了这种做法。

因为不同的版本,可以理解成同一种资源的不同表现形式,所以应该采用同一个URL。版本号可以在HTTP请求头信息的Accept字段中进行区分(参见Versioning REST Services):

Accept: vnd.example-com.foo+json; version=1.0

Accept: vnd.example-com.foo+json; version=1.1

Accept: vnd.example-com.foo+json; version=2.0

3. 路径(Endpoint)

路径又称"终点"(endpoint),表示API的具体网址,每个网址代表一种资源(resource)

(1) 资源作为网址,只能有名词,不能有动词,而且所用的名词往往与数据库的表名对应。

举例来说,以下是不好的例子:

/getProducts
/listOrders
/retreiveClientByOrder?orderId=1

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

GET /products :将返回所有产品清单
POST /products :将产品新建到集合
GET /products/4 :将获取产品 4
PATCH(或)PUT /products/4 :将更新产品 4

(2) API中的名词应该使用复数。无论子资源或者所有资源。

举例来说,获取产品的API可以这样定义

获取单个产品:http://127.0.0.1:8080/AppName/rest/products/1
获取所有产品: http://127.0.0.1:8080/AppName/rest/products

4. HTTP动词

对于资源的具体操作类型,由HTTP动词表示。

常用的HTTP动词有下面四个(括号里是对应的SQL命令)。

  • GET(SELECT):从服务器取出资源(一项或多项)。
  • POST(CREATE):在服务器新建一个资源。
  • PUT(UPDATE):在服务器更新资源(客户端提供改变后的完整资源)。
  • DELETE(DELETE):从服务器删除资源。

还有三个不常用的HTTP动词。

  • PATCH(UPDATE):在服务器更新(更新)资源(客户端提供改变的属性)。
  • HEAD:获取资源的元数据。
  • OPTIONS:获取信息,关于资源的哪些属性是客户端可以改变的。

下面是一些例子。

GET /zoos:列出所有动物园
POST /zoos:新建一个动物园(上传文件)
GET /zoos/ID:获取某个指定动物园的信息
PUT /zoos/ID:更新某个指定动物园的信息(提供该动物园的全部信息)
PATCH /zoos/ID:更新某个指定动物园的信息(提供该动物园的部分信息)
DELETE /zoos/ID:删除某个动物园
GET /zoos/ID/animals:列出某个指定动物园的所有动物
DELETE /zoos/ID/animals/ID:删除某个指定动物园的指定动物

5. 过滤信息(Filtering)

如果记录数量很多,服务器不可能都将它们返回给用户。API应该提供参数,过滤返回结果。

下面是一些常见的参数。query_string 查询字符串,地址栏后面问号后面的数据,格式: name=xx&sss=xxx

?limit=10:指定返回记录的数量
?offset=10:指定返回记录的开始位置。
?page=2&per_page=100:指定第几页,以及每页的记录数。
?sortby=name&order=asc:指定返回结果按照哪个属性排序,以及排序顺序。
?animal_type_id=1:指定筛选条件

参数的设计允许存在冗余,即允许API路径和URL参数偶尔有重复。比如,GET /zoos/ID/animals 与 GET /animals?zoo_id=ID 的含义是相同的。

/zoos/2/animals

/animals?zoo_id=2

6. 状态码(Status Codes)

服务器向用户返回的状态码和提示信息,常见的有以下一些(方括号中是该状态码对应的HTTP动词)。

  • 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 - [*]:服务器发生错误,用户将无法判断发出的请求是否成功。

状态码的完全列表参见这里或这里。

7. 错误处理(Error handling)

如果状态码是4xx,服务器就应该向用户返回出错信息。一般来说,返回的信息中将error作为键名,出错信息作为键值即可。

{
     
    error: "Invalid API key"
}

8. 返回结果

针对不同操作,服务器向用户返回的结果应该符合以下规范。

  • GET /collections:返回资源对象的列表(数组)
  • GET /collection/ID:返回单个资源对象(json)
  • POST /collection:返回新生成的资源对象(json)
  • PUT /collection/ID:返回完整的资源对象(json)
  • DELETE /collection/ID:返回一个空文档(空字符串)

9. 超媒体(Hypermedia API)

RESTful API最好做到Hypermedia(即返回结果中提供链接,连向其他API方法),使得用户不查文档,也知道下一步应该做什么。

比如,Github的API就是这种设计,访问api.github.com会得到一个所有可用API的网址列表。

{
     
"current_user_url": "https://api.github.com/user",
"authorizations_url": "https://api.github.com/authorizations",
// ...
}

从上面可以看到,如果想获取当前用户的信息,应该去访问api.github.com/user,然后就得到了下面结果。

{
     
  "message": "Requires authentication",
  "documentation_url": "https://developer.github.com/v3"
}

上面代码表示,服务器给出了提示信息,以及文档的网址。

10. 其他

服务器返回的数据格式,应该尽量使用JSON,避免使用XML。

3.Django Rest_Framework简介与安装

3.1 Django Rest_Framework简介

核心思想: 缩减编写api接口的代 – DRF

Django REST framework是一个建立在Django基础之上的Web 应用开发框架,可以快速的开发REST API接口应用。在REST framework中,提供了序列化器Serialzier的定义,可以帮助我们简化序列化与反序列化的过程,不仅如此,还提供丰富的类视图、扩展类、视图集来简化视图的编写工作。REST framework还提供了认证、权限、限流、过滤、分页、接口文档等功能支持。REST framework提供了一个API 的Web可视化界面来方便查看测试接口。

中文文档:https://q1mi.github.io/Django-REST-framework-documentation/#django-rest-framework

github: https://github.com/encode/django-rest-framework/tree/master

英文文档:https://www.django-rest-framework.org/

特点:

提供了定义序列化器Serializer的方法,可以快速根据 Django ORM 或者其它库自动序列化/反序列化;
提供了丰富的类视图、Mixin扩展类,简化视图的编写;
丰富的定制层级:函数视图、类视图、视图集合到自动生成 API,满足各种需要;
多种身份认证和权限认证方式的支持;[jwt]
内置了限流系统;
直观的 API web 界面;
可扩展性,插件丰富

3.2 环境安装与配置

DRF需要以下依赖:

  • Python (2.7, 3.2, 3.3, 3.4, 3.5, 3.6)
  • Django (1.10, 1.11, 2.0)

DRF是以Django扩展应用的方式提供的,所以我们可以直接利用已有的Django环境而无需从新创建。(若没有Django环境,需要先创建环境安装Django)

安装DRF

pip3 install django==2.2
pip3 install djangorestframework

4.drf创建项目流程

4.1 添加rest_framework应用

settings.pyINSTALLED_APPS中添加’rest_framework’。

INSTALLED_APPS = [
    ...
    'rest_framework',
]

接下来就可以使用DRF提供的功能进行api接口开发了。在项目中如果使用rest_framework框架实现API接口,主要有以下三个步骤:

  • 将请求的数据(如JSON格式)转换为模型类对象
  • 操作数据库
  • 将模型类对象转换为响应的数据(如JSON格式)

4.2 创建模型操作类

class Student(models.Model):
    # 模型字段
    name = models.CharField(max_length=100,verbose_name="姓名",help_text='提示文本:不能为空')
    sex = models.BooleanField(default=1,verbose_name="性别")
    age = models.IntegerField(verbose_name="年龄")
    class_null = models.CharField(max_length=5,verbose_name="班级编号")
    description = models.TextField(max_length=1000,verbose_name="个性签名")

    class Meta:
        db_table="tb_student"
        verbose_name = "学生"
        verbose_name_plural = verbose_name

创建数据库

create database students charset utf8;

主引用中__init__.py设置使用pymysql作为数据库驱动

import pymysql
pymysql.install_as_MySQLdb()

settings.py配置文件中设置mysql的账号密码

DATABASES = {
     
    # 'default': {
     
    #     'ENGINE': 'django.db.backends.sqlite3',
    #     'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    # },
    'default': {
     
        'ENGINE': 'django.db.backends.mysql',
        'NAME': "students",
        "HOST": "127.0.0.1",
        "PORT": 3306,
        "USER": "root",
        "PASSWORD":"123456",
    }
}

终端下,执行数据迁移

python manage.py makemigrations
python manage.py migrate

有如下报错:

从小白到高手---api接口和drf(Django Rest_Framework)使用超详解_第1张图片

解决方案:

注释掉 python/site-packages/django/backends/mysql/base.py中的35和36行代码。

在这里插入图片描述

从小白到高手---api接口和drf(Django Rest_Framework)使用超详解_第2张图片

解决方案:

backends/mysql/operations.py146行里面新增一个行代码:

query = query.encode()

4.3 创建序列化器

在students应用目录中新建serializers.py用于保存该应用的序列化器。

创建一个StudentModelSerializer用于序列化与反序列化。

from rest_framework.serializers import ModelSerializer
from students.models import Student
# 创建序列化器类,回头会在试图中被调用
class StudentModelSerializer(ModelSerializer):
    class Meta:
        model = Student
        fields = "__all__"
  • model 指明该序列化器处理的数据字段从模型类Student参考生成
  • fields 指明该序列化器包含模型类中的哪些字段,'all’指明包含所有字段

4.4 写视图

在students应用的views.py中创建视图StudentViewSet,这是一个视图集合

from rest_framework.viewsets import ModelViewSet
from .models import Student
from .serializers import StudentModelSerializer
# Create your views here.
class StudentViewSet(ModelViewSet):
    queryset = Student.objects.all() #指明该视图集在查询数据时使用的查询集
    serializer_class = StudentModelSerializer #指明该视图在进行序列化或反序列化时使用的序列化器
  • queryset 指明该视图集在查询数据时使用的查询集
  • serializer_class 指明该视图在进行序列化或反序列化时使用的序列化器

4.5 定义路由

在students应用的urls.py中定义路由信息。

from . import views
from rest_framework.routers import DefaultRouter

# 路由列表
urlpatterns = []

router = DefaultRouter()  # 可以处理视图的路由器,自动通过视图来生成增删改查的url路径
router.register('students', views.StudentViewSet)  #students是生成的url前缀,名称随便写, 向路由器中注册视图集

urlpatterns += router.urls  # 将路由器中的所以路由信息追到到django的路由列表中

最后把students子应用中的路由文件加载到总路由文件中.

from django.contrib import admin
from django.urls import path,include  #path:直接写路径,re_path:可以写正则匹配

urlpatterns = [
    path('admin/', admin.site.urls),
    path("stu/",include("students.urls")),
]

4.6 运行测试

运行该django项目

在浏览器输入路径,可以看到DRF提供的API Web浏览页面

在页面底部可以提交post请求,添加到数据库

在网址中输入路径/1,可以访问id为1的信息

在网址中输入路径/S/,可以访问删除的接口

从小白到高手---api接口和drf(Django Rest_Framework)使用超详解_第3张图片

5. 序列化器–Serializer

作用:

1. 序列化,序列化器会把模型对象转换成字典,经过response以后变成json字符串
2. 反序列化,把客户端发送过来的数据,经过request以后变成字典,序列化器可以把字典转成模型
3. 反序列化,完成数据校验功能

定义好Serializer类后,就可以创建Serializer对象了。

5.1 Serializer的构造方法

Serializer(instance=None, data=empty, **kwarg)

说明:

1)用于序列化时,将模型类对象传入instance参数

2)用于反序列化时,将要被反序列化的数据传入data参数

3)除了instance和data参数外,在构造Serializer对象时,还可通过context参数额外添加数据,如

serializer = AccountSerializer(account, context={
     'request': request})

通过context参数附加的数据,可以通过Serializer对象的context属性获取。

  1. 使用序列化器的时候一定要注意,序列化器声明了以后,不会自动执行,需要我们在视图中进行调用才可以。
  2. 序列化器无法直接接收数据,需要我们在视图中创建序列化器对象时把使用的数据传递过来。
  3. 序列化器的字段声明类似于我们前面使用过的表单系统。
  4. 开发restful api时,序列化器会帮我们把模型数据转换成字典.
  5. drf提供的视图会帮我们把字典转换成json,或者把客户端发送过来的数据转换字典.

5.2 序列化功能的使用

1 定义一个序列化器,在应用中创建一个py文件,serializers.py

from rest_framework import serializers
class StudentSerizlizer(serializers.Serializer):
    name = serializers.CharField()
    age = serializers.IntegerField()
    class_null = serializers.CharField()
    description = serializers.CharField()

2 写views.py文件

from django.shortcuts import render
from django.http import JsonResponse
from django.views import View
from app01 import models
from .serializers import StudentSerizlizer
# Create your views here.
class StudentView(View):
    def get(self,request):
        one = models.Student.objects.get(id=1)
        serializer = StudentSerizlizer(instance=one)
        print(serializer.data)  #结果为:{'name': 'ceshi1', 'age': 6, 'class_null': '1', 'description': '90886'}
        # all = models.Student.objects.all()
        # serializer = StudentSerizlizer(instance=all,many=True) #结果为:[{},{}]形式
        #many=True 在序列化的数据是多条时,需要加上many=True
        #print(serializer.data)
        #结果为:[OrderedDict([('name', 'ceshi1'), ('age', 6), ('class_null', '1'), ('description', '90886.....
        return JsonResponse(serializer.data,safe=False,json_dumps_params={
     'ensure_ascii':False})
    	# JsonResponse回复的就是Json类型数据,JsonResponse响应非字典类型数据时,需要加上safe=False这个参数才行,json_dumps_params在浏览器中显示中文

3 配置路由

from django.urls import path
from use_serializers import views
urlpatterns = [
    path('students/', views.StudentView.as_view())
]
from django.contrib import admin
from django.urls import path, include #path:直接写路径,re_path:可以写正则匹配

urlpatterns = [
    path('admin/', admin.site.urls),
    path('app01/', include('app01.urls')),
    path('use01/', include('use_serializers.urls')),
]

测试结果:

从小白到高手---api接口和drf(Django Rest_Framework)使用超详解_第4张图片

5.3 反序列化功能的使用

5.3.1 一个简单的实例

1 定义一个序列化器,在当前应用中新建serializers.py

from rest_framework import serializers
class StudentSerializers(serializers.Serializer):
    name = serializers.CharField(max_length=4) #校验长度是否是4
    age = serializers.IntegerField(max_value=18) #校验长度是否是18
    class_null = serializers.CharField()
    description = serializers.CharField(allow_blank=True) #允许该字段不填,allow_null用null来存空值

2 写views.py文件

from django.shortcuts import render,HttpResponse
from django.http import JsonResponse
from django.views import View
from .serializers import StudentSerializers
# Create your views here.
class StuView(View):
    def post(self,request):
        data  = {
     
            'name':request.POST.get('name'),
            'age' : request.POST.get('age'),
            'class_null':request.POST.get('class_null'),
            'description':request.POST.get('description'),
        }
        ser = StudentSerializers(data=data)
        if ser.is_valid():  #校验通过为True
            print(ser.validated_data) #校验通过的正确信息
        print(ser.errors) #校验未通过的错误信息
        return HttpResponse('ok')

3 url:

from django.urls import path
from use_unserializers import views
urlpatterns = [
    path('students/',views.StuView.as_view())
]

5.3.2 全局钩子和局部钩子

1 定义一个序列化器,在当前应用中新建serializers.py

from rest_framework import serializers

def check01(val):
    if '666' in val :
        raise serializers.ValidationError('错误!')
    else:
        return val
class StudentSerializers(serializers.Serializer):
    name = serializers.CharField(max_length=4,validators=[check01,])
    age = serializers.IntegerField(max_value=18)
    class_null = serializers.IntegerField()
    description = serializers.CharField(allow_blank=True)
    #局部钩子 validate_字段名:针对单个字段进行校验
    def validate_name(self,val):
        if '777' in val :
            raise serializers.ValidationError('错误!!')
        return val
    #全局钩子 validate : 针对多个属性数据进行校验
    def validate(self, data):
        print(data)
        age = data.get('age')
        print(age)
        class_null = data.get('class_null')
        print(class_null)
        if age == class_null:
            raise serializers.ValidationError('错误!!!!')
        return data

执行顺序:

# is_valid()方法时,先校验序列化器类中的所有属性Field(CharField(参数))参数对应的校验规则
# 再执行局部钩子
# 举例:比如有name和age两个属性,先执行name 的参数校验,在执行name的局部钩子(validate_name(self,val)),然后再执行age属性的参数校验,再执行age的局部钩子,最后所有属性的参数校验和局部钩子校验完成之后,执行全局钩子

# 最后执行全局钩子

2 views.py文件

from django.shortcuts import render,HttpResponse
from django.http import JsonResponse
from django.views import View
from .serializers import StudentSerializers
from app01 import models
# Create your views here.
class StuView(View):
    def post(self,request):
        data  = {
     
            'name':request.POST.get('name'),
            'age' : request.POST.get('age'),
            'class_null':request.POST.get('class_null'),
            'description':request.POST.get('description'),
        }
        ser = StudentSerializers(data=data)
        if ser.is_valid():  #校验通过为True
            print(ser.validated_data) #打印正确信息
            ret = models.Student.objects.create(
                **ser.validated_data #将正确的信息写入数据库
            )
            serializer = StudentSerializers(instance=ret) #序列化
            return JsonResponse(serializer.data,safe=False,json_dumps_params={
     'ensure_ascii':False}) #返回新增数据
        print(ser.errors) #打印错误信息
        return JsonResponse(ser.errors) #返回错误信息

5.3.3 序列化器一些常用参数的说明

常用字段类型
字段 字段构造方式
BooleanField BooleanField()
NullBooleanField NullBooleanField()
CharField CharField(max_length=None, min_length=None, allow_blank=False, trim_whitespace=True)
EmailField EmailField(max_length=None, min_length=None, allow_blank=False)
RegexField RegexField(regex, max_length=None, min_length=None, allow_blank=False)
SlugField SlugField(maxlength=50, min_length=None, allow_blank=False) 正则字段,验证正则模式 [a-zA-Z0-9-]+
URLField URLField(max_length=200, min_length=None, allow_blank=False)
UUIDField UUIDField(format=‘hex_verbose’) format: 1) 'hex_verbose'"5ce0e9a5-5ffa-654b-cee0-1238041fb31a" 2) 'hex'"5ce0e9a55ffa654bcee01238041fb31a" 3)'int' - 如: "123456789012312313134124512351145145114" 4)'urn' 如: "urn:uuid:5ce0e9a5-5ffa-654b-cee0-1238041fb31a" 微软时间戳,通过微秒生成一个随机字符串
IPAddressField IPAddressField(protocol=‘both’, unpack_ipv4=False, **options)
IntegerField IntegerField(max_value=None, min_value=None)
FloatField FloatField(max_value=None, min_value=None)
DecimalField DecimalField(max_digits, decimal_places, coerce_to_string=None, max_value=None, min_value=None) max_digits: 最多位数 decimal_palces: 小数点位置
DateTimeField DateTimeField(format=api_settings.DATETIME_FORMAT, input_formats=None)
DateField DateField(format=api_settings.DATE_FORMAT, input_formats=None)
TimeField TimeField(format=api_settings.TIME_FORMAT, input_formats=None)
DurationField DurationField()
ChoiceField ChoiceField(choices) choices与Django的用法相同
MultipleChoiceField MultipleChoiceField(choices)
FileField FileField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL)
ImageField ImageField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL)
ListField ListField(child=, min_length=None, max_length=None)
DictField DictField(child=)
选项参数:
参数名称 作用
max_length 最大长度
min_length 最小长度
allow_blank 是否允许为空
trim_whitespace 是否截断空白字符
max_value 最大值
min_value 最小值
通用参数:
参数名称 说明
read_only 表明该字段仅用于序列化输出,默认False
write_only 表明该字段仅用于反序列化输入,默认False
required 表明该字段在反序列化时必须输入,默认True
default 反序列化时使用的默认值
allow_null 表明该字段是否允许传入None,默认False
validators 该字段使用的验证器
error_messages 包含错误编号与错误信息的字典
label 用于HTML展示API页面时,显示的字段名称
help_text 用于HTML展示API页面时,显示的字段帮助提示信息
raise_exception参数:

等于True会主动抛异常:

在验证失败时抛出异常serializers.ValidationError,可以通过传递raise_exception=True参数开启,REST framework接收到此异常,会向前端返回HTTP 400 Bad Request响应。

serializer.is_valid(raise_exception=True)
read_only和write_only参数的区别:
from rest_framework import serializers

class StudentSerizlizer(serializers.Serializer):
    id = serializers.IntegerField(read_only=True)
    # read_only=True,序列化时序列化出该字段数据,反序列化校验时不要校验这个数据
    name = serializers.CharField(max_length=4,)
    age = serializers.IntegerField(max_value=18,write_only=True)
    # write_only=True,序列化时不序列化这个字段数据,反序列校验时,需要客户端传递这个数据过来进行校验
    class_null = serializers.CharField()
    description = serializers.CharField(allow_blank=True)
partial参数:

只更新传入的数据

def put(self,request):
    data = {
     
        'name':'chao',
        'age':18
    }
    serializer = StudentSerizlizer(data=data,partial=True)
    # partial=True:只需要校验传入给序列化器的数据,适用于部分数据更新
    serializer.is_valid()
    print(serializer.errors)
    print('正确数据:', serializer.validated_data)

    return JsonResponse({
     'ss': 'kk'})

5.3.4 用序列器进行增改操作

**增:**在序列器中定义create方法,在视图中通过save来触发

class StudentSerizlizer(serializers.Serializer):
    # 
    name = serializers.CharField(max_length=4,validators=[check666,])
 	...
    
    def create(self, validated_data):

        # self.validated_data
        # validated_data
        # print('>>',self.validated_data)
        print('>>',validated_data)
        ret = models.Student.objects.create(
            **validated_data
        )
        return ret

视图中通过save方法来触发,序列化器类中的create方法
	data = {
     
            'name':request.POST.get('name'),
            'age':request.POST.get('age'),
            'class_null':request.POST.get('class_null'),
            'description':request.POST.get('description'),
        }
    ser = StudentSerizlizer(data=data)  
    if ser.is_valid():
        instance = ser.save()  #得到create方法的返回值
        serializer = StudentSerizlizer(instance=instance)
        return JsonResponse(serializer.data,safe=False,json_dumps_params={
     'ensure_ascii':False})
    # return JsonResponse({'xx':'xx'})

**改:**在序列化器中定义更新方法,在视图函数中通过save保存

class StudentSerizlizer(serializers.Serializer):
    # 
    name = serializers.CharField(max_length=4,validators=[check666,])
 	...
    # 做更新动作
    def update(self, instance, validated_data):

        # print(instance)
        # print(validated_data)
        instance.name = validated_data['name']
        instance.age = validated_data['age']
        instance.class_null = validated_data['class_null']
        instance.description = validated_data['description']
        instance.save()
        return instance

视图
	   def put(self,request):

        data = {
     
            'id': 2,
            'name':'xx',
            'age':8,
            'sex':0,
            'class_null':'9',
            'description':'xx',
        }
        obj = models.Student.objects.get(id=data.get('id'))
        s_obj = StudentSerizlizer(instance=obj,data=data)  # 实例化序列化器类时,传入instance参数,
        # 参数partial=True:只需要校验传入给序列化器的数据,适用于部分数据更新
        if s_obj.is_valid():
            s_obj.save()  #触发序列化器类的update方法
        else:
            return JsonResponse({
     'error':'数据错误'})

5.4 ModelSerializer的使用

序列化与反序列化

1 定义一个序列化器,继承serializers.ModelSerializer:

from rest_framework import serializers
from students import models
class StudentModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Student #声明对哪个模型类对象进行操作
        fields = '__all__'     #全部取出
        # fields =  ['id', 'name', 'age'] #只取id name age 字段
        # exclude = ['name','age'] #除了name,age字段,其他的取出
        extra_kwargs = {
          #为字段添加额外的参数
            'id':{
     'read_only':True,},
            'age':{
     'write_only':True,}
        }

2 视图文件:

from django.shortcuts import render
from django.http import JsonResponse
# Create your views here.
from django.views import View
from .serializers import StudentModelSerializer
from students import models
class StudentView(View):

    def get(self,request):
        all = models.Student.objects.all()
        model_ser = StudentModelSerializer(instance=all,many=True)
        return JsonResponse(model_ser.data, safe=False, json_dumps_params={
     'ensure_ascii':False})

    def post(self,request):
        print(request.POST)
        obj = StudentModelSerializer(data=request.POST)
        if obj.is_valid():
            print(obj.validated_data)
            obj.save()  #自动能够帮我们进行数据保存
            return JsonResponse({
     'msg':'success'})
        print(obj.errors)
        print(obj.validated_data)
        return JsonResponse({
     'xx':'ssssss'})

6.视图

6.1 request对象和response对象

request对象

在get方法中request.GET<=>request.query_params

在post方法中:

​ 当发送的数据格式为…urlencoded类型时,request.POST<=>request.data

​ 当发送过来的是json类型数据时,我们使用request.data属性能够获取到数据,得到的是普通字典类型。(如果是多选数据,不能使用getlist方法获取)

from rest_framework.views import APIView

class UserView(APIView):

    def get(self,request):
        print(request.GET)
        print(request.query_params)
		# request.GET和request.query_params是等价的
        return JsonResponse({
     'xx':'xxxxx'})

    def post(self,request):
        print(request)
        # 当发送的数据格式为..urlencoded类型时,request.POST和request.data等价
        # print(request.POST.getlist('hobby')) #
        # 当发送过来的是json类型数据时,我们使用request.data属性能够获取到数据
        print(request.data) #{'username': 'xxxx', 'password': '123'},普通字典类型
        # request.data能够获取到客户端发送过来的json类型数据,但是得到的结果为普通字典类型,但是如果是多选数据,不能使用getlist方法获取

        return JsonResponse({
     'xx': 'xxxxx'})

response对象

查看drf的默认配置
from rest_framework import settings

引入response
from rest_framework.response import Response

Response默认响应的是json数据类型
当用浏览器访问时看到页面效果.


用户配置替换drf内部配置的写法
REST_FRAMEWORK = {
     
    'DEFAULT_RENDERER_CLASSES': (  # 默认响应渲染类
        'rest_framework.renderers.JSONRenderer',  # json渲染器
        'rest_framework.renderers.BrowsableAPIRenderer',  # 浏览器API渲染器
    )
}

通过浏览器访问:

从小白到高手---api接口和drf(Django Rest_Framework)使用超详解_第5张图片

注释浏览器API渲染器(得到的就是一个Json对象):

从小白到高手---api接口和drf(Django Rest_Framework)使用超详解_第6张图片

response的方法的相关参数

Response(data, status=None, template_name=None, headers=None, content_type=None)
data: 响应的序列化数据。
status: 响应的状态代码。默认为200。
template_name: 选择 HTMLRenderer 时使用的模板名称。
headers: 设置 HTTP header,字典类型。
content_type: 响应的内容类型,通常渲染器会根据内容协商的结果自动设置,但有些时候需要手动指定。
Response 可以将内置类型转换成字符串。写到data的位置

响应状态码

from rest_framework import status

# return Response({'xx':'xxxxx'}, status=201)
return Response({
     'xx':'xxxxx'}, status=status.HTTP_201_CREATED)

所有状态码:

HTTP_100_CONTINUE = 100
HTTP_101_SWITCHING_PROTOCOLS = 101
HTTP_200_OK = 200
HTTP_201_CREATED = 201
HTTP_202_ACCEPTED = 202
HTTP_203_NON_AUTHORITATIVE_INFORMATION = 203
HTTP_204_NO_CONTENT = 204
HTTP_205_RESET_CONTENT = 205
HTTP_206_PARTIAL_CONTENT = 206
HTTP_207_MULTI_STATUS = 207
HTTP_208_ALREADY_REPORTED = 208
HTTP_226_IM_USED = 226
HTTP_300_MULTIPLE_CHOICES = 300
HTTP_301_MOVED_PERMANENTLY = 301
HTTP_302_FOUND = 302
HTTP_303_SEE_OTHER = 303
HTTP_304_NOT_MODIFIED = 304
HTTP_305_USE_PROXY = 305
HTTP_306_RESERVED = 306
HTTP_307_TEMPORARY_REDIRECT = 307
HTTP_308_PERMANENT_REDIRECT = 308
HTTP_400_BAD_REQUEST = 400
HTTP_401_UNAUTHORIZED = 401
HTTP_402_PAYMENT_REQUIRED = 402
HTTP_403_FORBIDDEN = 403
HTTP_404_NOT_FOUND = 404
HTTP_405_METHOD_NOT_ALLOWED = 405
HTTP_406_NOT_ACCEPTABLE = 406
HTTP_407_PROXY_AUTHENTICATION_REQUIRED = 407
HTTP_408_REQUEST_TIMEOUT = 408
HTTP_409_CONFLICT = 409
HTTP_410_GONE = 410
HTTP_411_LENGTH_REQUIRED = 411
HTTP_412_PRECONDITION_FAILED = 412
HTTP_413_REQUEST_ENTITY_TOO_LARGE = 413
HTTP_414_REQUEST_URI_TOO_LONG = 414
HTTP_415_UNSUPPORTED_MEDIA_TYPE = 415
HTTP_416_REQUESTED_RANGE_NOT_SATISFIABLE = 416
HTTP_417_EXPECTATION_FAILED = 417
HTTP_418_IM_A_TEAPOT = 418
HTTP_422_UNPROCESSABLE_ENTITY = 422
HTTP_423_LOCKED = 423
HTTP_424_FAILED_DEPENDENCY = 424
HTTP_426_UPGRADE_REQUIRED = 426
HTTP_428_PRECONDITION_REQUIRED = 428
HTTP_429_TOO_MANY_REQUESTS = 429
HTTP_431_REQUEST_HEADER_FIELDS_TOO_LARGE = 431
HTTP_451_UNAVAILABLE_FOR_LEGAL_REASONS = 451
HTTP_500_INTERNAL_SERVER_ERROR = 500
HTTP_501_NOT_IMPLEMENTED = 501
HTTP_502_BAD_GATEWAY = 502
HTTP_503_SERVICE_UNAVAILABLE = 503
HTTP_504_GATEWAY_TIMEOUT = 504
HTTP_505_HTTP_VERSION_NOT_SUPPORTED = 505
HTTP_506_VARIANT_ALSO_NEGOTIATES = 506
HTTP_507_INSUFFICIENT_STORAGE = 507
HTTP_508_LOOP_DETECTED = 508
HTTP_509_BANDWIDTH_LIMIT_EXCEEDED = 509
HTTP_510_NOT_EXTENDED = 510
HTTP_511_NETWORK_AUTHENTICATION_REQUIRED = 511

6.2 个视图集类–基于APIView视图

基于APIView的五个接口
	get--查询所有
	post--提交
	get(...pk)--查询单条数据
	put(...pk)--更新单条数据
	delete(...pk)--删除单条数据

新建一个应用,定义一个序列器,

from rest_framework import serializers
from app01 import models

class StudentModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Student
        fields = '__all__'

路由:

from django.urls import path, re_path
from use_view import views
urlpatterns = [
    path('student1/', views.StudentViews11.as_view()),
    re_path(r'student1/(?P\d+)/', views.StudentViews12.as_view()),
]

视图:

from rest_framework.views import APIView
from app01 import models
from .serializers import StudentModelSerializer
from rest_framework.response import Response
from rest_framework import status
# Create your views here.

class StudentViews11(APIView):
    def get(self,request):
        all = models.Student.objects.all()
        ser = StudentModelSerializer(instance=all,many=True)
        return Response(ser.data)
    def post(self,request):
        data = request.data
        ser = StudentModelSerializer(data=data)
        if ser.is_valid():
            instance = ser.save()
            ser = StudentModelSerializer(instance=instance)
            return Response(ser.data,status=status.HTTP_201_CREATED)
        return Response(ser.errors)
class StudentViews12(APIView):
    def get(self,request,pk):
        one = models.Student.objects.get(pk=pk)
        ser = StudentModelSerializer(instance=one)
        return Response(ser.data)
    def put(self,request,pk):
        data = request.data
        one = models.Student.objects.get(pk=pk)
        ser = StudentModelSerializer(instance=one,data=data,partial=True)
        if ser.is_valid():
            instance = ser.save()
            ser = StudentModelSerializer(instance=instance)
            return Response(ser.data)
        return Response(ser.errors)
    def delete(self,request,pk):
        models.Student.objects.get(pk=pk).delete()
        return Response(None,status=status.HTTP_204_NO_CONTENT)

6.3 基于GenericAPIView视图

简单来说,相比与APIView,GenericAPIView拓展了:

​ 1 可以指定使用的数据查询集。属性:queryset

​ 通过self.get_queryset()–>获取所有查询集

​ 通过self.get_object()–>获取单个查询对象,(传入的参数是pk,可通过lookup_field修改)

​ 2 可以指定使用的序列器。属性:serializer_class

​ 通过self.get_serializer()–>调用序列器

​ 通过get_serializer_class,控制不同条件下,使用不同的序列化器类

def get_serializer_class(self):
    if self.request.method == 'GET':
        return Student2Serializer
    else:
        return StudentSerializer

实例(用GenericAPIView实现五个接口):

路由:

from django.urls import path, re_path
from use_view import views
urlpatterns = [
    path('student1/', views.StudentViews11.as_view()),
    re_path(r'student1/(?P\d+)/', views.StudentViews12.as_view()),

    path('student2/', views.StudentViews21.as_view()),
    re_path(r'student2/(?P\d+)/', views.StudentViews22.as_view()),
]

视图:

from rest_framework.generics import GenericAPIView
class StudentViews21(GenericAPIView):
    queryset = models.Student.objects.all()
    serializer_class = StudentModelSerializer
    def get(self,request):
        ser = self.get_serializer(instance=self.get_queryset(),many=True)
        return Response(ser.data)
    def post(self,request):
        data = request.data
        ser = self.get_serializer(data=data)
        if ser.is_valid():
            instance = ser.save()
            ser = self.get_serializer(instance=instance)
            return Response(ser.data,status=status.HTTP_201_CREATED)
        return Response(ser.errors)
class StudentViews22(GenericAPIView):
    queryset = models.Student.objects.all()
    serializer_class = StudentModelSerializer
    def get(self,request,pk):
        ser = self.get_serializer(instance=self.get_object())
        return Response(ser.data)
    def put(self,request,pk):
        ser = self.get_serializer(instance=self.get_object(),data=request.data,partial=True)
        if ser.is_valid():
            instance = ser.save()
            ser = self.get_serializer(instance=instance)
            return Response(ser.data)
        return Response(ser.errors)
    def delete(self,requset,pk):
        self.get_object().delete()
        return Response(None,status=status.HTTP_204_NO_CONTENT)

6.4 基于Mixin个视图扩展类视图

需要配合GenericAPIView使用,因为五个扩展类的实现需要调用GenericAPIView提供的序列化器与数据库查询的方法

相比于GenericAPIView,Mixin拓展封装了:调用序列器序列化与反序列化的过程

ListModelMixin:self.list(request)

CreateModelMixin:self.create(request)

RetrieveModelMixin:self.retrieve(request,pk)

UpdateModelMixin:self.update(request,pk)

DestroyModelMixin:self.destroy(request,pk)

实例(实现五个接口):

urls.py:

from django.urls import path, re_path
from use_view import views
urlpatterns = [
    path('student1/', views.StudentViews11.as_view()),
    re_path(r'student1/(?P\d+)/', views.StudentViews12.as_view()),

    path('student2/', views.StudentViews21.as_view()),
    re_path(r'student2/(?P\d+)/', views.StudentViews22.as_view()),

    path('student3/', views.StudentViews31.as_view()),
    re_path(r'student3/(?P\d+)/', views.StudentViews32.as_view()),
]

views.py:

from rest_framework.generics import GenericAPIView
from rest_framework.mixins import ListModelMixin,CreateModelMixin,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin
class StudentViews31(GenericAPIView,ListModelMixin,CreateModelMixin):
    queryset = models.Student.objects.all()
    serializer_class = StudentModelSerializer
    def get(self,request):
        return self.list(request)
    def post(self,request):
        return self.create(request)
class StudentViews32(GenericAPIView,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin):
    queryset = models.Student.objects.all()
    serializer_class = StudentModelSerializer
    def get(self,request,pk):
        return self.retrieve(request,pk)
    def put(self,request,pk):
        return self.update(request,pk)
    def delete(self,request,pk):
        return self.destroy(request,pk)

6.5 基于GenericAPIView视图

ListAPIView:get请求,继承 GenericAPIView、ListModelMixin

CreateAPIView:post请求,继承GenericAPIView、CreateModelMixin

RetrieveAPIView:带参数的get请求,继承GenericAPIView、RetrieveModelMixin

UpdateAPIView:put请求,继承GenericAPIView、UpdateModelMixin

DestroyAPIView:delete请求,继承GenericAPIView、DestoryModelMixin

路由:

from django.urls import path, re_path
from use_view import views
urlpatterns = [
    path('student1/', views.StudentViews11.as_view()),
    re_path(r'student1/(?P\d+)/', views.StudentViews12.as_view()),

    path('student2/', views.StudentViews21.as_view()),
    re_path(r'student2/(?P\d+)/', views.StudentViews22.as_view()),

    path('student3/', views.StudentViews31.as_view()),
    re_path(r'student3/(?P\d+)/', views.StudentViews32.as_view()),

    path('student4/', views.StudentViews41.as_view()),
    re_path(r'student4/(?P\d+)/', views.StudentViews42.as_view()),

]

视图:

from rest_framework.generics import ListAPIView,CreateAPIView,RetrieveAPIView,UpdateAPIView,DestroyAPIView
class StudentViews41(ListAPIView,CreateAPIView):
    queryset = models.Student.objects.all()
    serializer_class = StudentModelSerializer
class StudentViews42(RetrieveAPIView,UpdateAPIView,DestroyAPIView):
    queryset = models.Student.objects.all()
    serializer_class = StudentModelSerializer

6.6 基于ViewSet视图集基类视图

使用视图集ViewSet,可以将一系列逻辑相关的动作放到一个类中

ViewSet视图集类不再实现get()、post()等方法,而是实现动作 action 如 list() 、create() 等。

视图集只在使用as_view()方法的时候,才会将action动作与具体请求方式对应上。

路由:

from use_view import views
urlpatterns = [
    path('student4/', views.StudentViews41.as_view()),
    re_path(r'student4/(?P\d+)/', views.StudentViews42.as_view()),

    path('student5/', views.StudentView5.as_view({
     'get': 'get_all_student', 'post': 'add_student'})),
    re_path(r'student5/(?P\d+)/', views.StudentView5.as_view({
     'get': 'get_one'})),

]

视图:

from rest_framework.viewsets import ViewSet
class StudentView5(ViewSet):
    def get_all_student(self,request):
        all = models.Student.objects.all()
        ser = StudentModelSerializer(instance=all,many=True)
        return Response(ser.data)
    def add_student(self,request):
        data = request.data
        ser = StudentModelSerializer(data=data)
        if ser.is_valid():
            instance = ser.save()
            ser = StudentModelSerializer(instance=instance)
            return Response(ser.data,status=status.HTTP_201_CREATED)
        return Response(ser.errors)
    def get_one(self,request,pk):
        one = models.Student.objects.get(pk=pk)
        ser = StudentModelSerializer(instance=one)
        return Response(ser.data)

6.7 基于GenericViewSet视图

相比于ViewSet,GenericViewSet拓展了queryset,serializer_class属性

路由:

from django.urls import path, re_path
from use_view import views
urlpatterns = [
    path('student4/', views.StudentViews41.as_view()),
    re_path(r'student4/(?P\d+)/', views.StudentViews42.as_view()),

    path('student5/', views.StudentView5.as_view({
     'get': 'get_all_student', 'post': 'add_student'})),
    re_path(r'student5/(?P\d+)/', views.StudentView5.as_view({
     'get': 'get_one'})),

    path('student6/', views.StudentView6.as_view({
     'get': 'get_all_student', 'post': 'add_student'})),
    re_path(r'student6/(?P\d+)/', views.StudentView6.as_view({
     'get': 'get_one'})),
]

视图:

from rest_framework.viewsets import GenericViewSet
class StudentView6(GenericViewSet):
    queryset = models.Student.objects.all()
    serializer_class = StudentModelSerializer
    def get_all_student(self,request):
        all = self.get_queryset()
        ser = self.get_serializer(instance=all,many=True)
        return Response(ser.data)
    def add_student(self,request):
        data = request.data
        ser = self.get_serializer(data=data)
        if ser.is_valid():
            instance = ser.save()
            ser = self.get_serializer(instance=instance)
            return Response(ser.data,status=status.HTTP_201_CREATED)
        return Response(ser.errors)
    def get_one(self,request,pk):
        one = self.get_object()
        ser = self.get_serializer(instance=one)
        return Response(ser.data)

6.8 GenericViewSet与mixins配合视图

路由:

from django.urls import path, re_path
from use_view import views
urlpatterns = [
    path('student4/', views.StudentViews41.as_view()),
    re_path(r'student4/(?P\d+)/', views.StudentViews42.as_view()),

    path('student5/', views.StudentView5.as_view({
     'get': 'get_all_student', 'post': 'add_student'})),
    re_path(r'student5/(?P\d+)/', views.StudentView5.as_view({
     'get': 'get_one'})),

    path('student6/', views.StudentView6.as_view({
     'get': 'get_all_student', 'post': 'add_student'})),
    re_path(r'student6/(?P\d+)/', views.StudentView6.as_view({
     'get': 'get_one'})),

    path('student7/', views.StudentView7.as_view({
     'get': 'get_all_student', 'post': 'add_student'})),
    re_path(r'student7/(?P\d+)/', views.StudentView7.as_view({
     'get': 'get_one','put':'update_one','delete':'delete_one'})),
]

视图:

from rest_framework.viewsets import GenericViewSet
from rest_framework.mixins import ListModelMixin,CreateModelMixin,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin
class StudentView7(GenericViewSet,ListModelMixin,CreateModelMixin,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin):
    queryset = models.Student.objects.all()
    serializer_class = StudentModelSerializer
    def get_all_student(self, request):
        return self.list(request)
    def add_student(self, request):
        return self.create(request)
    def get_one(self, request, pk):
        return self.retrieve(request, pk)
    def update_one(self, request, pk):
        return self.update(request, pk)
    def delete_one(self, request, pk):
        return self.destroy(request, pk)

6.9 基于ModelViewSet视图

在路由中直接定义:{‘get’: ‘list’, ‘post’: ‘create’…},可以简化代码省去类中的自定义方法

ModelViewSet:继承了GenericViewSet与mixins,故代码如下:

路由:

from django.urls import path, re_path
from use_view import views
urlpatterns = [
    path('student7/', views.StudentView7.as_view({
     'get': 'get_all_student', 'post': 'add_student'})),
    re_path(r'student7/(?P\d+)/', views.StudentView7.as_view({
     'get': 'get_one','put':'update_one','delete':'delete_one'})),

    path('student8/', views.StudentView8.as_view({
     'get': 'list', 'post': 'create'})),
    re_path(r'student8/(?P\d+)/', views.StudentView8.as_view({
     'get':'retrieve','put':'update','delete':'destroy'})),

]

视图:

from rest_framework.viewsets import ModelViewSet
class StudentView8(ModelViewSet):
    queryset = models.Student.objects.all()
    serializer_class = StudentModelSerializer

7.路由routers

7.1DefaultRouter和SimpleRouter

作用:将视图中类的路径自动注册

from rest_framework import routers
router = routers.DefaultRouter()
#router = routers.SimpleRouter()
router.register('dd',views.StudentView8)
urlpatterns += router.urls

7.2action装饰器

用于自定义的方法

from rest_framework.viewsets import ModelViewSet
from rest_framework.decorators import action

class StudentModelViewSet(ModelViewSet):
    queryset = Student.objects.all()
    serializer_class = StudentModelSerializer

    # methods 设置当前方法允许哪些http请求访问当前视图方法
    # detail 设置当前视图方法是否是操作一个数据
    # detail为True,表示路径名格式应该为 router_stu/{pk}/login/
    @action(methods=['get'], detail=True)
    def login(self, request,pk):
        """登录"""
        ...

    # detail为False 表示路径名格式应该为 router_stu/get_new_5/
    @action(methods=['put'], detail=False)
    def get_new_5(self, request):
        """获取最新添加的5个学生信息"""
        ...

8.认证Authentication

8.1 创建管理员用户及后台管理系统简单使用

python manage.py createsuperuser

从小白到高手---api接口和drf(Django Rest_Framework)使用超详解_第7张图片

可以在setting文件中配置后台管理系统为中文

LANGUAGE_CODE = 'zh-hans'

从小白到高手---api接口和drf(Django Rest_Framework)使用超详解_第8张图片

访问http://127.0.0.1:8000/admin就可以看到后台admin站点

admin后台管理系统简单使用:

在应用的admin.py文件中

from app01 import models

class StudentAdmin(admin.ModelAdmin):
    list_display = ['id','name','age','class_null'] #展示哪些字段
    list_editable = ['name','age'] #可编辑哪些字段

admin.site.register(models.Student,StudentAdmin)

url:

path('admin/', admin.site.urls),

8.2 自定义认证Authentication

1 在配置文件中配置全局默认的认证方案

该认证方案与后台管理系统是一套系统。

在setting文件中可以配置:

REST_FRAMEWORK = {
     
    'DEFAULT_AUTHENTICATION_CLASSES': (
        #哪个写在前面,优先使用哪个认证
        'rest_framework.authentication.SessionAuthentication', # session认证,admin后台其实就使用的session认证,其实接口开发很少用到session认证,所以我们通过配置可以改为其他认证,比如后面项目里面我们用到jwt,JSON WEB TOKEN认证,或者一些配合redis的认证
        'rest_framework.authentication.BasicAuthentication',  # 基本认证,工作当中可能一些测试人员会参与的话,他们会将一些认证数据保存在内存当中,然后验证的,我们基本上用不上
    ),
}

2 在当前应用下新建一个文件夹,utils/auth.py

from rest_framework.authentication import BaseAuthentication #引入基础的认证rest_framework基础的认证类
from rest_framework.exceptions import AuthenticationFailed #引入认证失败的错误
class Auth(BaseAuthentication):
    def authenticate(self, request): #authenticate可以扩展的方法
        username = 'root' #request为请求对象
        if username == 'root':
            return 'root','xx'
        else:
            raise AuthenticationFailed('登录失败!!')
  1. 1配置全局生效。在setting文件中注册该自定义的类
REST_FRAMEWORK = {
     

    'DEFAULT_AUTHENTICATION_CLASSES': (
        #哪个写在前面,优先使用哪个认证
        'use_view.utils.auth.Auth', #路径,配置的是全局
        'rest_framework.authentication.SessionAuthentication', 
        'rest_framework.authentication.BasicAuthentication', 
    ),
}

3.2 配置局部生效。在局部视图中定义authentication_classes属性

from rest_framework.viewsets import GenericViewSet
from rest_framework.mixins import ListModelMixin,CreateModelMixin,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin
from use_view.utils.auth import Auth
class StudentView7(GenericViewSet,ListModelMixin,CreateModelMixin,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin):
    queryset = models.Student.objects.all()
    serializer_class = StudentModelSerializer
    authentication_classes = [Auth, ] #在本视图类中生效
    def get_all_student(self, request):
        print(request.user)
        print(request.auth)
        return self.list(request)
    def add_student(self, request):
        return self.create(request)
    def get_one(self, request, pk):
        return self.retrieve(request, pk)
    def update_one(self, request, pk):
        return self.update(request, pk)
    def delete_one(self, request, pk):
        return self.destroy(request, pk)

9.权限Permissions

权限控制可以限制用户对于视图的访问和对于具体数据对象的访问。

  • 在执行视图的dispatch()方法前,会先进行视图访问权限的判断
  • 在通过get_object()获取具体对象时,会进行模型对象访问权限的判断

9.1 配置权限

1 全局配置:在setting文件中配置REST_FRAMEWORK

REST_FRAMEWORK = {
     
    ....
    
    'DEFAULT_PERMISSION_CLASSES': (
        'rest_framework.permissions.IsAuthenticated', #登录状态下才能访问我们的接口,可以通过退出admin后台之后,你看一下还能不能访问我们正常的接口就看到效果了
    )
}
  • AllowAny 允许所有用户,未指明时默认为允许所有用户

    ​ from rest_framework import settings可以看出

  • IsAuthenticated 仅通过认证的用户

  • IsAdminUser 仅管理员用户(可以通过admin创建一个用户进行测试)

  • IsAuthenticatedOrReadOnly 已经登陆认证的用户可以对数据进行增删改操作,没有登陆认证的只能查看数据。

从小白到高手---api接口和drf(Django Rest_Framework)使用超详解_第9张图片

2 局部配置:在视图中添加属性permission_classes

from rest_framework.authentication import SessionAuthentication
from rest_framework.permissions import IsAuthenticated
from rest_framework.generics import RetrieveAPIView

class StudentAPIView(RetrieveAPIView):
    queryset = Student.objects.all()
    serializer_class = StudentSerializer
    authentication_classes = [SessionAuthentication]
    permission_classes = [IsAuthenticated]

9.2 自定义权限

如需自定义权限,需继承rest_framework.permissions.BasePermission父类,并实现以下两个任何一个方法或全部

  • .has_permission(self, request, view)

    是否可以访问视图, view表示当前视图对象

  • .has_object_permission(self, request, view, obj)

    是否可以访问数据对象, view表示当前视图, obj为数据对象

例如:

1 在当前子应用下,创建一个权限文件permissions.py中声明自定义权限类:

from rest_framework.permissions import BasePermission
class MyPermission(BasePermission):
    def has_permission(self, request, view):
        if request.user.username == 'xx':
            return True
        return False

2 在视图中添加属性

from rest_framework.viewsets import ModelViewSet
from .utils.auth import MyPermission
class StudentView8(ModelViewSet):
    queryset = models.Student.objects.all()
    serializer_class = StudentModelSerializer
    permission_classes = [MyPermission]

从小白到高手---api接口和drf(Django Rest_Framework)使用超详解_第10张图片

10 限流Throttling

可以对接口访问的频次进行限制,以减轻服务器压力。

一般用于付费购买次数,投票等场景使用.

10.1 配置限流

全局配置

在setting文件中配置REST_FRAMEWORK

REST_FRAMEWORK = {
     
    ...
    
    'DEFAULT_THROTTLE_CLASSES': (
        'rest_framework.throttling.AnonRateThrottle',  # 匿名用户,未登录的
        'rest_framework.throttling.UserRateThrottle'  # 经过登录之后的用户
    ),
    'DEFAULT_THROTTLE_RATES': {
     
        'anon': '5/m',
        'user': '3/m'
    }
}
#{'s': 1, 'm': 60, 'h': 3600, 'd': 86400} m表示分钟,可以写m,也可以写minute

从小白到高手---api接口和drf(Django Rest_Framework)使用超详解_第11张图片

局部配置

1 在setting文件中配置REST_FRAMEWORK

    'DEFAULT_THROTTLE_RATES': {
     
        'anon': '5/m',
        'user': '3/m',
    }

在某个视图中添加throttle_classess属性来配置

from rest_framework.viewsets import ModelViewSet
from rest_framework.throttling import UserRateThrottle
class StudentView8(ModelViewSet):
    queryset = models.Student.objects.all()
    serializer_class = StudentModelSerializer
    throttle_classes = [UserRateThrottle,]

从小白到高手---api接口和drf(Django Rest_Framework)使用超详解_第12张图片

10.2 参数说明

可选限流类

1) AnonRateThrottle

限制所有匿名未认证用户,使用IP区分用户。

使用DEFAULT_THROTTLE_RATES['anon'] 来设置频次

2)UserRateThrottle

限制认证用户,使用User id 来区分。

使用DEFAULT_THROTTLE_RATES['user'] 来设置频次

3)ScopedRateThrottle (待定…)

限制用户对于每个视图的访问频次,使用ip或user id,先找的用户id,没有设置用户id的话就会使用ip地址。

例如: 全局

class ContactListView(APIView):
    throttle_scope = 'contacts'
    ...

class ContactDetailView(APIView):
    throttle_scope = 'contacts'
    ...

class UploadView(APIView):
    throttle_scope = 'uploads'
    ...
-------------------------------------------------
REST_FRAMEWORK = {
     
    'DEFAULT_THROTTLE_CLASSES': (
        'rest_framework.throttling.ScopedRateThrottle',
    ),
    'DEFAULT_THROTTLE_RATES': {
     
        'contacts': '1000/day',
        'uploads': '20/day'
    }
}

10.3 ScopedRateThrottle局部使用示例

1 setting文件中配置

    'DEFAULT_THROTTLE_RATES': {
     
        'anon': '5/m',
        'user': '3/m',
        'xx': '3/m',
    }

2 视图

from rest_framework.viewsets import ModelViewSet
from .utils.auth import MyPermission
from rest_framework.throttling import ScopedRateThrottle
class StudentView8(ModelViewSet):
    queryset = models.Student.objects.all()
    serializer_class = StudentModelSerializer
    permission_classes = [MyPermission]
    throttle_classes = [ScopedRateThrottle,]
    throttle_scope = 'xx'

11 过滤Filtering

对于列表数据可能需要根据字段进行过滤,我们可以通过添加django-filter扩展来增强支持。

安装django-filter:

pip3 install django-filter

1 配置setting文件(注册应用,配置REST_FRAMEWORK)

INSTALLED_APPS = [
 	...
    'rest_framework',
    'app01',
    'use_serializers',
    'use_unserializers',
    'use_view',
    'django_filters', #注册应用
]

REST_FRAMEWORK = {
     
	...
    'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',)

}

2 视图:增加filter_fields属性,将需要过滤的字段传参。

from rest_framework.viewsets import ModelViewSet

class StudentView8(ModelViewSet):
    queryset = models.Student.objects.all()
    serializer_class = StudentModelSerializer
    filter_fields = ('age','sex','id') #增加filter_fields属性

访问:http://127.0.0.1:8000/use03/student8/?id=1即可得到id为1的值

12 排序

对于列表数据,REST framework提供了OrderingFilter过滤器来帮助我们快速指明数据按照指定字段进行排序。

12.1 排序的使用

在视图中

​ 1 添加filter_backends属性(使用OrderingFilter过滤器)

​ 2 指定字段,通过ordering_fields属性指定需要按某字段排序

from rest_framework.viewsets import ModelViewSet
from rest_framework.filters import OrderingFilter
class StudentView8(ModelViewSet):
    queryset = models.Student.objects.all()
    serializer_class = StudentModelSerializer
    filter_backends = [OrderingFilter]
    ordering_fields = ('id','age')

在网址中输入

http://127.0.0.1:8000/use03/student8/?ordering=-id(按id字段倒叙)

12.2 排序和过滤的混合使用

在视图中

from rest_framework.viewsets import ModelViewSet
from rest_framework.filters import OrderingFilter
from django_filters.rest_framework import DjangoFilterBackend

class StudentView8(ModelViewSet):
    queryset = models.Student.objects.all()
    serializer_class = StudentModelSerializer
    filter_fields = ('age','sex','id')
    # 因为filter_backends是局部过滤配置,局部配置会覆盖全局配置,所以需要重新把过滤组件核心类再次声明,
    # 否则过滤功能会失效
    filter_backends = [OrderingFilter,DjangoFilterBackend]
    ordering_fields = ('id','age')

如不加声明,过滤功能会失效:

从小白到高手---api接口和drf(Django Rest_Framework)使用超详解_第13张图片

13 分页Pagination

REST framework提供了分页的支持。

13.1配置全局分页

在setting文件中配置REST_FRAMEWORK(一般不用,list方法都会触发)

REST_FRAMEWORK = {
     
  # 全局分页,一旦设置了全局分页,那么我们drf中的视图扩展类里面的list方法提供的列表页都会产生分页的效果。所以一般不用全局分页
    'DEFAULT_PAGINATION_CLASS':  'rest_framework.pagination.PageNumberPagination',
    'PAGE_SIZE': 100  # 每页最大数据量
}

在视图中关闭分页功能,只需在视图中添加属性pagination_class

pagination_class = None

13.2 配置局部分页

继承PageNumberPagination

在视图中定义一个类,继承PageNumberPagination

from rest_framework.pagination import PageNumberPagination,LimitOffsetPagination
class LargeResultsSetPagination(PageNumberPagination):
    #page_size = 2  #默认每一页显示的数据量
    # http://127.0.0.1:8000/use03/student8/?pp=1&size=3
    page_query_param = 'pp' # 自定义页码的参数名
    page_size_query_param = 'size'  #允许客户端通过get参数来控制每一页的数据量
    max_page_size = 5 #客户端通过size指定获取数据的条数时,最大不能超过多少
    
    
from rest_framework.viewsets import ModelViewSet
class StudentView8(ModelViewSet):
    queryset = models.Student.objects.all()
    serializer_class = StudentModelSerializer
    pagination_class = LargeResultsSetPagination

从小白到高手---api接口和drf(Django Rest_Framework)使用超详解_第14张图片

继承LimitOffsetPagination

就是通过偏移量(索引)来取数据

如:GET http://127.0.0.1/four/students/?limit=100&offset=400 #从下标为400的记录开始,取100条记录

from rest_framework.pagination import LimitOffsetPagination
class LargePagination(LimitOffsetPagination): #其实就是通过偏移量来取数据
    default_limit = 3  #一页显示3条
    
from rest_framework.viewsets import ModelViewSet
class StudentView8(ModelViewSet):
    queryset = models.Student.objects.all()
    serializer_class = StudentModelSerializer
    pagination_class = LargePagination    

从小白到高手---api接口和drf(Django Rest_Framework)使用超详解_第15张图片

参数说明:

1. default_limit 默认限制,每页数据量大小,默认值与`PAGE_SIZE`设置一致
2. limit_query_param limit参数名,默认'limit' , 可以通过这个参数来改名字
3. offset_query_param offset参数名,默认'offset' ,可以通过这个参数来改名字
4. max_limit 最大limit限制,默认None, 无限制

14 异常处理Exceptions

14.1 自定义异常的捕获

先看一个简单的实例:

视图:

class ApiError(Exception):
    pass

class Test(APIView):
    def get(self,request,pk):
        try:
            one = models.Student.objects.get(pk=pk)
        except models.Student.DoesNotExist:
            raise ApiError('查询出错!!!')
        ser = StudentModelSerializer(instance=one)
        return Response(ser.data)

当索引超出范围后,会抛出错误:

从小白到高手---api接口和drf(Django Rest_Framework)使用超详解_第16张图片

在REST framework中提供了异常处理,我们可以自定义异常处理函数,来捕获异常反馈前端

1 在应用下新建一个文件exceptions.py,

from rest_framework.views import exception_handler
from .views import ApiError
from rest_framework.response import Response
def custom_exception_handler(exc, context): #自定义的错误处理函数
    # 先调用REST framework默认的异常处理方法获得标准错误响应对象
    response = exception_handler(exc, context) #这个函数是drf提供的,它处理了一些错误,但是如果它处理不了的,它会返回None,所以,如果是None的话,我们需要自己来处理错误
    # 在此处补充自定义的异常处理
    if response is None:
        if isinstance(exc,ApiError):
            #这里就可以记录错误信息了,一般记录到文件中,可以使用日志系统来进行记录
            return Response({
     'msg':'自定义API错误了'})
            #response.data['status_code'] = response.status_code
    return response

2 在setting文件中配置REST_FRAMEWORK

REST_FRAMEWORK = {
     
    'EXCEPTION_HANDLER': 'my_project.my_app.utils.custom_exception_handler'
}

这样就完成了自定义异常的捕获:

从小白到高手---api接口和drf(Django Rest_Framework)使用超详解_第17张图片

同理,补充上处理关于数据库的异常:

from rest_framework.views import exception_handler as drf_exception_handler
from rest_framework import status
from django.db import DatabaseError

def exception_handler(exc, context):
    response = drf_exception_handler(exc, context)

    if response is None:
        view = context['view'] #出错的方法或者函数名称
        if isinstance(exc, DatabaseError):
            print('[%s]: %s' % (view, exc))
            response = Response({
     'detail': '服务器内部错误'}, status=status.HTTP_507_INSUFFICIENT_STORAGE)

    return response

14.2 REST framework定义的异常

  • APIException 所有异常的父类
  • ParseError 解析错误
  • AuthenticationFailed 认证失败
  • NotAuthenticated 尚未认证
  • PermissionDenied 权限决绝
  • NotFound 未找到
  • MethodNotAllowed 请求方式不支持
  • NotAcceptable 要获取的数据格式不支持
  • Throttled 超过限流次数
  • ValidationError 校验失败

也就是说,上面列出来的异常不需要我们自行处理了,很多的没有在上面列出来的异常,就需要我们在自定义异常中自己处理了。

15 自动生成接口文档

15.1 如何自动生成接口文档

需要3步:

1 安装依赖,REST framewrok生成接口文档需要coreapi库的支持。

pip3 install coreapi

2 配置路由

from django.urls import path, include #path:直接写路径,re_path:可以写正则匹配
from rest_framework.documentation import include_docs_urls

urlpatterns = [
    ...
    path('docs/', include_docs_urls(title='站点页面标题'))
]

3 配置setting文件REST_FRAMEWORK

'DEFAULT_SCHEMA_CLASS': "rest_framework.schemas.AutoSchema"

这样一个接口文档就自动生成了。

15.2 文档描述说明的定义位置

1) 单一方法的视图,可直接使用类视图的文档字符串,如

class BookListView(generics.ListAPIView):
    """
    get: 返回所有图书信息.
    post: 添加记录
    """
    #注意,这是在类中声明的注释,如果在方法中你声明了其他注释,会覆盖这个注释的

2)包含多个方法的视图,在类视图的文档字符串中,分开方法定义,如

class BookListCreateView(generics.ListCreateAPIView):
    """
    get:
    返回所有图书信息.

    post:
    新建图书.
    """

3)对于视图集ViewSet,仍在类视图的文档字符串中封开定义,但是应使用action名称区分,如

class BookInfoViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin, GenericViewSet):
    """
    list:
    返回图书列表数据

    retrieve:
    返回图书详情数据

    latest:
    返回最新的图书数据

    read:
    修改图书的阅读量
    """

从小白到高手---api接口和drf(Django Rest_Framework)使用超详解_第18张图片

4) 视图集ViewSet中的retrieve名称,在接口文档网站中叫做read

从小白到高手---api接口和drf(Django Rest_Framework)使用超详解_第19张图片

5)参数的Description需要在模型类或序列化器类的字段中以help_text选项定义

从小白到高手---api接口和drf(Django Rest_Framework)使用超详解_第20张图片

从小白到高手---api接口和drf(Django Rest_Framework)使用超详解_第21张图片

或 注意,如果你多个应用使用同一个序列化器,可能会导致help_text的内容显示有些问题,小事情

class StudentSerializer(serializers.ModelSerializer):
    class Meta:
        model = Student
        fields = "__all__"
        extra_kwargs = {
     
            'age': {
     
                'required': True,
                'help_text': '年龄'
            }
        }

16 Xadmin

16.1 安装

需要4步,如下:

1 通过如下命令安装xadmin的最新版。

pip3 install https://codeload.github.com/sshwsfc/xadmin/zip/django2

2 在setting配置文件中注册。

INSTALLED_APPS = [
    ...
    'xadmin',
    'crispy_forms',
    'reversion',
    ...
]

# 修改使用中文界面
LANGUAGE_CODE = 'zh-Hans'

# 修改时区
TIME_ZONE = 'Asia/Shanghai'

3 数据库迁移。xadmin有建立自己的数据库模型类,需要进行数据库迁移

python manage.py makemigrations
python manage.py migrate

4 添加路由。在总路由中添加

import xadmin
xadmin.autodiscover()

# version模块自动注册需要版本控制的 Model
from xadmin.plugins import xversion
xversion.register_models()

urlpatterns = [
    path(r'xadmin/', xadmin.site.urls),
]

另外,如果之前没有创建超级用户,需要创建,如果有了,则可以直接使用之前的

python manage.py createsuperuser

16.2 使用

  • xadmin不再使用Django的admin.py,而是需要编写代码在adminx.py文件中,每一个应用都可以写一创建adminx.py对xadmin站点进行配置。
  • xadmin的站点管理类不用继承admin.ModelAdmin,而是直接继承object即可。

例如:在子应用中创建adminx.py文件。

站点的全局配置

from xadmin import views
from app01 import models

class BaseSetting(object):
    """xadmin的基本配置"""
    enable_themes = True  # 开启主题切换功能
    use_bootswatch = True # 引导控制盘(其实就是我们的左侧菜单栏)
xadmin.site.register(views.BaseAdminView, BaseSetting)

class GlobalSettings(object):
    """xadmin的全局配置"""
    site_title = "路飞学城"  # 设置站点标题
    site_footer = "路飞学城有限公司"  # 设置站点的页脚
    menu_style = "accordion"  # 设置菜单折叠
xadmin.site.register(views.CommAdminView, GlobalSettings)

class StudentXadmin(object):
    list_display = ['id','name','age','class_null']
    list_editable = ['name','age']
xadmin.site.register(models.Student,StudentXadmin)

站点Model管理

xadmin可以使用的页面样式控制基本与Django原生的admin一直。

可以在models类中定义个__str__方法来定义对象显示成什么内容

  • list_display 控制列表展示的字段

    list_display = ['id', 'btitle', 'bread', 'bcomment']
    
  • search_fields 控制可以通过搜索框搜索的字段名称,xadmin使用的是模糊查询

    search_fields = ['id','btitle']
    
  • list_filter 可以进行过滤操作的列,对于分类、性别、状态

    list_filter = ['is_delete']
    
  • ordering 默认排序的字段

    ordering = ['-age',]  #-倒序
    
  • show_detail_fields 在列表页提供快速显示详情信息

    show_detail_fields = ['id',]
    
  • list_editable 在列表页可以快速直接编辑的字段

    list_editable = ['name','age',]
    
  • refresh_times 指定列表页的定时刷新

    refresh_times = [5, 10,30,60]  # 设置允许后端管理人员按多长时间(秒)刷新页面,选好之后就能自动刷新了
    
    
  • list_export 控制列表页导出数据的可选格式

    list_export = ('xls', 'json','csv')#写元祖或者列表都行   list_export设置为None来禁用数据导出功能
    list_export_fields = ('id', 'btitle', 'bpub_date') #设置允许导出的字段
    
  • show_bookmarks 控制是否显示书签功能

    show_bookmarks = True #False就隐藏了这个功能
    
  • data_charts 控制显示图表的样式

    data_charts = {
           
            "order_amount": {
             #随便写的名称order_amount
              'title': '图书发布日期表', 
              "x-field": "bpub_date", 
              "y-field": ('btitle',),
              "order": ('id',),
              
            },
        #    支持生成多个不同的图表
        #    "order_amount2": {
           
        #      'title': '图书发布日期表', 
        #      "x-field": "bpub_date", 
        #      "y-field": ('btitle',),
        #      "order": ('id',)
        #    },
        }
    
    • title 控制图标名称
    • x-field 控制x轴字段
    • y-field 控制y轴字段,可以是多个值
    • order 控制默认排序
  • model_icon 控制菜单的图标【图标的设置可以参考font-awesome的图标css名称】

    model_icon = 'fa fa-gift'
    
  • readonly_fields 在编辑页面的只读字段

    readonly_fields = ['name',]
    
  • exclude 在编辑页面隐藏的字段,比如判断这个数据是否删除的delete_status字段,一般就是用来标识一下字段是不是被删除了,但是数据库中不删除

    exclude = ['name',]
    
  • 这并不是所有功能,可以参看它的文档,它提供的一些功能我们可能还需要自定制,调整或者添加一些它没有的功能,后面再说

你可能感兴趣的:(django,python,django,api,接口,后端)