Django Rest_Framework
1.web应用模式
在开发web应用中,有两种应用模式:
- 前后端不分离
- 前后端分离
2.API接口
为了在团队内部形成共识,防止个人习惯差异引起的混乱,我们需要找到一种大家都觉的很好的接口实现规范,而且这种规范能够让后端写的接口用途一目了然,减少双方之间的合作成本。
目前市面上大部分公司开发人员使用的接口服务架构主要有:restful,rpc
- rpc:翻译成中文:远程过程调用[远程服务调用]
接口多了,对应函数名和参数就多了,前端在请求api接口时,就会比较难找,容易出现重复的接口
- restful:翻译成中文:资源状态转换
把后端所有的数据/文件都看成资源
那么接口请求数据,本质上来说就是对资源的 操作
web项目中操作资源,无非就是增删改查,所以要求在地址栏中声明要操作的资源是什么,然后通过http请求
动词来说明对资源进行哪一种操作。
3.RESTful API规范
REST全称是Representational State Transfer,中文意识是表述性状态转移。它首次出现在2000年Roy Fielding的博士论文中。
RESTful是一种定义web API接口的设计风格,尤其是适用于前后端分离的应用模式中。
这种风格的理念认为后端开发任务就是提供数据的,对外提供的是数据资源的访问接口,所以在定义接口时,客户端访问的URL路径就表示这种要操作的数据资源。
而对于数据资源分别使用POST,DELETE,GET,UPDATE等请求动作来表达对数据的增删改查
请求方法 | 请求地址 | 后端操作 |
---|---|---|
GET | /students | 获取所有学生 |
POST | /students | 增加学生 |
GET | /students/ |
获取编号为pk的学生 |
PUT | /students/ |
修改编号为pk的学生 |
DELETE | /students/ |
删除编号为pk的学生 |
事实上,我们可以使用任何一个框架都可以实现符合restful规范的API接口
参考文档:http://www.runoob.com/w3cnote/restful-architecture.html
4.序列化
api接口开发,最核心最常见的一个过程就是序列化,所谓序列化就是把数据转换格式,序列化可以分两个阶段:
序列化:把我们识别的数据转换成指定格式提供给别人。
例如:我们在django中获取到的数据默认是模型对象,但是模型对象数据无法直接提供给前端或别的平台使用,所以我们需要把数据进行序列化,变成字符串或json数据,提供给别人。
反序列化:把别人提供的数据转换/还原成我们需要的格式
例如:前端js提供过来的json数据,对于python而言就是字符串,我们需要进行反序列化转换成模型类对象数据,这样我们才能把数据保存到数据库中。
5.Django Rest_Framework
核心思想:缩减编写api接口的代码量(drf)
Django REST framework是一个建立在Django基础上的Web应用开发框架,可以快速的开发REST API接口应用。在REST framework中,提供了序列化器Serializer的定义,可以帮助我们简化序列化与反序列化的过程,不仅如此,还提供了丰富的类视图,扩展类,视图集来简化视图的编写工作,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,满足各种需要
- 多种身份认证和权限认证方式的支持
- 内置了限流系统
- 直观的API web界面
- 可扩展性,插件丰富
6. 环境安装与配置
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)
6.1 安装DRF
pip3 install django==2.2
pip3 install djangorestframework
6.1.1创建django项目
cd ~/Desktop
django-admin startproject drfemo
也可以直接在pycharm中创建django项目
6.2 添加rest_framework应用
在settings.py配置文件中的INSTALLED_APPS中添加'rest_framework'
INSTALLED_APPS = [
...
'rest_framework',
]
接下来就可以使用DRF提供的功能进行api接口开发了,在项目中如果使用rest_framework框架实现API接口,主要有以下三个步骤:
- 将请求的数据(如JSON格式)转换为模型类对象
- 操作数据库
- 将模型类对象转换为相应的数据(如JSON格式)
6.3 DRF简写代码的过程
6.3.1 创建模型操作类
models.py
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
mysql中创建一个数据库方便测试
create database drf01 charset utf8;
6.3.1.1 执行数据迁移
新建的应用要添加到INSTALL_APPS中去
初始化数据库连接
安装pymysql
pip install pymysql
主项目的__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":"123",
}
}
pycharm或终端下执行数据迁移
python mamage.py makemigrations
python manage.py migrate
错误列表
# 执行数据迁移 python manage.py makemigrations 报错如下:
解决方案:
注释掉 python/site-packages/django/db/backends/mysql/base.py中的35和36行代码。
执行数迁移发生以下错误:
解决方法:
backends/mysql/operations.py146行里面新增一个行代码:
6.3.2 创建序列化器
创建一个新应用
python manage.py startapp students
在students应用目录中新建serializers.py用于保存该应用的序列化器
from rest_framework import serializers
from app01 import models
class StudentSerializer(serializers.ModelSerializer):
class Meta:
model = models.Student
fields = "__all__"
extra_kwargs = {
'id':{'read_only':True},
'age':{'write_only':True},
}
- model指明该序列化器处理的数据字段从模型类Student参考生成
- fields指明该序列化器包含模型类中的哪些字段,all指明包含所有字段
6.3.3 编写视图
在students应用的views.py中创建视图StudentView,这是一个视图集合
from django.shortcuts import render
from rest_framework.views import APIView
# Create your views here.
from app01 import models
from rest_framework import status
from .serializers import StudentSerializer,Student1Serializer
from rest_framework.response import Response
class Students1View(APIView):
# 获取所有数据借口
def get(self,request):
all_data = models.Student.objects.all()
serializer = StudentSerializer(instance=all_data,many=True)
return Response(serializer.data)
# 添加一条记录的接口
def post(self,request):
data = request.data
serializer = StudentSerializer(data=data)
if serializer.is_valid():
instance = serializer.save()
serializer = StudentSerializer(instance=instance)
return Response(serializer.data,status=status.HTTP_201_CREATED)
else:
return Response({'xxx':'有字段错误'})
6.3.4 定义路由
students应用中urls.py中定义路由信息
from django.urls import path,re_path
from app01 import views
urlpatterns = [
path('students1/',views.Students1View.as_view()),
#re_path('students1/(?P<pk>d+)/',views.Student1View.as_view()),
]
总路由urls.py的配置
from django.contrib import admin
from django.urls import path,re_path,include
urlpatterns = [
path('admin/', admin.site.urls),
path('stu/',include('students.urls'))
]
6.3.5 运行测试
运行当前程序(与运行Django一样)
python manage.py runserver 端口号
在浏览器中输入网址127.0.0.1:8000/stu,可以看到DRF提供的API Web浏览页面:
在浏览器中输入网址127.0.0.1:8000/stu/students1/
就可以对数据库内容进行增删改查的操作了,很简单
7.序列化器
作用:
1. 序列化,序列化器会把模型对象转换成字典,经过response以后变成json字符串
2. 反序列化,把客户端发送过来的数据,经过request以后变成字典,序列化器可以把字典转换成模型
3. 反序列化,完成数据校验功能
7.1 定义序列化器
Django REST framework中的Serializer使用类来定义,须继承自rest_framework.serializers.Serializer
from rest_framework import serializers
# 声明序列化器,所有的序列化器都要直接或者间接继承于 Serializer
# 其中,ModelSerializer是Serializer的子类,ModelSerializer在Serializer的基础上进行了代码简化
class StudentSerializer(serializers.Serializer):
"""学生信息序列化器"""
# 1. 需要进行数据转换的字段
id = serializers.IntegerField()
name = serializers.CharField()
age = serializers.IntegerField()
sex = serializers.BooleanField()
description = serializers.CharField()
# 2. 如果序列化器集成的是ModelSerializer,则需要声明调用的模型信息
# 3. 验证提交的数据的代码
# 4. 编写添加和更新模型的代码
注意:serializer不是只能为数据库模型类定义,也可以为非数据库模型类的数据定义,serializer是独立于数据库之外的存在
常用字段类型:
字段 | 字段构造方式 |
---|---|
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页面时,显示的字段帮助提示信息 |
7.2 创建serializer对象
定义好serializer类后,就可以创建serializer对象了
serializer的构造方法为
serializer(instance=None,data=empty,**kwargs)
说明:
-
用于序列化时,将模型类对象传入instance参数
-
用于反序列化时,将要被反序列化的数据传入data参数
-
除了instance和data参数外,在构造serializer对象时,还渴通过context参数额外添加数据
serializer = AccountSerializer(account,context={'request':request})
通过context参数附加的数据,可以通过serializer对象的context属性获取
注意点"
1. 使用序列化器的时候一定要注意,序列化器声明了以后,不会自动执行,需要我们在视图中进行调用才可以 2. 序列化器无法直接接收数据,需要我们在视图中创建序列化器对象时把使用的数据传递过来 3. 序列化器的字段声明类似于表单系统 4. 开发restful api时,序列化器会帮我们把模型数据转换成字典 5. drf提供的视图会帮我们把字典转换成json,或者客户端发送过来的数据转换成字典
7.3 序列化器的使用
7.3.1序列化
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 在视图中使用
from django.shortcuts import render,HttpResponse
from django.http import JsonResponse
from students import models
# Create your views here.
from django.views import View
from .serializers import StudentSerizlizer
class StudentView(View):
def get(self,request):
# all = models.Student.objects.all() #quseryset model
one = models.Student.objects.get(id=1) #quseryset model
# serializer = StudentSerizlizer(all,many=True)
# serializer = StudentSerizlizer(all) #结果为:[{},{}]形式
serializer = StudentSerizlizer(one) #得到的结果为字典 data
print(serializer.data) #{'name': 'chao', 'age': 18, 'class_null': '31', 'description': 'xxxxx'}
return JsonResponse(serializer.data,safe=False,json_dumps_params={'ensure_ascii':False})
3 应用配置文件
INSTALLED_APPS = [
...
'students.apps.StudentsConfig',
'rest_framework',
'ser'
]
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'drf01',
'HOST':'127.0.0.1',
'PORT':3306,
'USER':'root',
'PASSWORD':'123456'
}
}
7.3.2反序列化功能
1 创建反序列化组件
在应用中创建一个py文件,比如叫做serializers.py
from rest_framework import serializers
# 自定义校验函数
def check666(val):
if '666' in val:
raise serializers.ValidationError('不能光喊6666啊,没有用')
else:
return val
class StudentSerizlizer(serializers.Serializer):
name = serializers.CharField(max_length=4,validators=[check666,])
age = serializers.IntegerField(max_value=18)
class_null = serializers.CharField()
# description = serializers.CharField(required=False,allow_null=True)
# required=False,allow_null=True允许字段为空,也就是不用传递过来这个data
description = serializers.CharField(allow_blank=True)
#allow_blank=True 允许只为空字符串
2 视图中使用
class Student(View):
def get..
...
def post(self,request):
# content-type
# if content-type == 'urlencoded':
# #username=chao&password=123&a=1
# request.POST['username'] = 'chao'
# elif content-type == 'form-data':
# # 分片接受数据
# request.FILES
# django没有自带解析json数据的解析器,所以需要我们手动的解析,但是drf中已经有了
# content - type == 'application/json'
#
print(request.POST)
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)
print(ser.is_valid()) #校验,全部通过得到True,一个字段错了都是得到False
print(ser.errors) #所有字段的错误信息
# print(request.body)
return HttpResponse('ok')
7.4 全局钩子和局部钩子的使用
ser/serializers.py
class StudentSerizlizer(serziliers.Serializer)
#定义局部钩子 :单个属性的校验
def validate_name(self,val): # 固定写法 校验哪个字段后面就写哪个字段
print('xxxx>>>',val)
if '777' in val:
raise serializers.ValidationError('不能有777') # 有错误信息就不会往数据库中存储数据,可以把添加数据的代码注掉看报错信息
return val # 如果没有错误,要返回这个了数据
# 全局钩子 : 主要是针对多个属性进行校验
def validate(self,data)
print(data)
age = data.get('age')
class_null = data.get('class_null')
if age == class_null:
raise serializers.ValidationError('age和class_null不能相等')
return data # 如果没有错误,全局钩子需要return所有数据
# 执行顺序
# 执行is_valid()方法时,先校验序列化器类中的所有属性Field(CharField(参数))参数对应的校验规则
# 再执行局部钩子
# 举例: 比如有name和age两个属性,先执行name属性的参数校验,再执行name的局部钩子
(validate_name(self,val)),然后 在执行age属性的参数校验,再执行age的局部钩子,最后所有属性的参数校验和局部钩子校验完成之后,执行全局钩子
# 最后执行全局钩子
视图部分
def post(self,request):
#
print(request.POST)
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():
print(request.body)
print(ser.validated_data) #返回验证成功后的数据
ret = models.Student.objects.create(
**ser.validated_data
)
# 按照接口规范,如果post请求提交保存了一条数据,那么要将新添加的数据再响应回去
serializer = StudentSerizlizer(instance=ret)
print(serializer.data) #得到的是教研成功之后的所有正确数据
return JsonResponse(serializer.data,safe=False,json_dumps_params={'ensure_ascii':False})
# return JsonResponse({'xx':'xx'})
else:
return JsonResponse({'error':'有字段错误'})
常用字段类型:
字段 | 字段构造方式 |
---|---|
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会主动抛异常
serializer.is_valid(raise_exception=True)
保存数据
方式1 直接在视图中保存
if ser.is_valid():
# print(request.body)
ret = models.Student.objects.create(
**ser.validated_data
)
serializer = StudentSerizlizer(instance=ret)
return JsonResponse(serializer.data,safe=False,json_dumps_params={'ensure_ascii':False})
else:
print(ser.errors)
return JsonResponse({'error':'有字段错误'})
方式2
在序列化器中定义create方法来数据的保存
class StudentSerizlizer(serializers.Serializer):
# <QueryDict: {'name': ['c777'], 'age': ['6'], 'description': ['123'], 'class_null': ['1']}>
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
更新数据
在序列化器中定义update方法来数据的更新
class StudentSerizlizer(serializers.Serializer):
# <QueryDict: {'name': ['c777'], 'age': ['6'], 'description': ['123'], 'class_null': ['1']}>
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参数
if s_obj.is_valid():
s_obj.save() #触发序列化器类的update方法
else:
return JsonResponse({'error':'数据错误'})
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)
视图部分
from django.shortcuts import render
from django.views import View
# Create your views here.
from students import models
from .serializers import StudentSerizlizer
from django.http import JsonResponse
class StudentView(View):
def get(self,request):
all = models.Student.objects.all()
serializer = StudentSerizlizer(instance=all, many=True)
return JsonResponse(serializer.data, safe=False, json_dumps_params={'ensure_ascii':False})
def post(self,request):
print(request.POST)
data = {
'name':request.POST.get('name'),
'sex':request.POST.get('sex'),
'age':request.POST.get('age'),
'class_null':request.POST.get('class_null'),
'description':request.POST.get('description'),
}
serializer = StudentSerizlizer(data=data)
serializer.is_valid()
print(serializer.errors)
print('正确数据:',serializer.validated_data)
return JsonResponse({'xx':'xx'})
partial参数:默认为False
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'})
modelSerializer的使用(自动帮我们保存数据不需要在写 create,update等方法)
序列化和反序列化的使用
定义如下的类(从数据库中获取约束属性自动生成serlializers的字段)
from rest_framework import serializers
from students import models
class StudentModelSerializer(serializers.ModelSerializer):
class Meta:
model = models.Student
fields = '__all__'
# fields = ['id', 'name', 'age']
# fields = ['id', 'name', 'age']
# exclude = ['name','age']
extra_kwargs = {
'id':{'read_only':True,},
'age':{'write_only':True,}
}
'''
id
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="个性签名")
'''
'''
id = serializers.IntegerField(read_only=True)
name = serializers.CharField(max_length=6,) # name char(6)
age = serializers.IntegerField(max_value=18,write_only=True)
sex = serializers.BooleanField()
class_null = serializers.CharField()
description = serializers.CharField(allow_blank=True)
'''
视图当中使用
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)
print()
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'})
modelserializer进行数据保存时的问题
8. 视图相关
rest framework 提供了众多的通用视图基类与扩展类,以简化视图的编写
8.1 两个视图基类
8.1.1 APIView类
from rest_framework.views import APIView
APIView
是REST framework提供的所有视图的基类,继承自Django的View
父类。
APIVIew
与View
的不同知促在于:
- 传入到视图方法中的是REST framework的
Request
对象,而不是Django的HttpRequest
对象 - 视图方法可以返回REST framework的
Response
对象,视图会为响应数据设置符合前端要求的格式 - 任何
APIException
异常都会被捕获到,并且处理成合适的响应信息 - 在进行dispatch()分发前,会对请求进行身份认证,权限检查,流量控制
支持定义的类属性
- authentication_classes列表或元组,身份认证类
- permission_classes列表或元组,权限检查类
- throttle_classes列表或元组,流量控制类
APIView
的request对象
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')) #<QueryDict: {'name': ['小林2'], 'age': ['6']}>
# 当发送过来的是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渲染器
)
}
response的方法的相关参数
Response(data, status=None, template_name=None, headers=None, content_type=None)
响应状态码
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
template_name,用于自定制浏览器的响应页面,了解
headers定制响应头键值对
return Response({'xx':'xxxxx'}, status=status.HTTP_201_CREATED,headers={'xx':'oo'})
在APIView
中仍一常规的类视图定义方法来实现get,post()或者其他请求方式的方法
举例:常见一个应用,写5个接口
from django.shortcuts import render
from rest_framework.views import APIView
# Create your views here.
from students import models
from .serializers import StudentModelSerializer
from rest_framework.response import Response
from rest_framework import status
class StudentsAPIView(APIView):
def get(self,request):
"""
获取所有学生数据
:param request:
:return:
"""
"""
简单总结:
1 获取一个数据,传instance参数
获取多条数据,传instance,many=True参数
2 更新数据,传instance和data参数
部分字段更新,可以增加partial=True参数
3 添加数据,传data参数
4 删除数据,不需要使用序列化器
"""
data_list = models.Student.objects.all()
serializer = StudentModelSerializer(instance=data_list,many=True)
return Response(serializer.data)
def post(self,request):
"""
添加学生信息
:param requeset:
:return:
"""
data = request.data
serializer = StudentModelSerializer(data=data)
serializer.is_valid(raise_exception=True)
instance = serializer.save()
# 将添加的新的数据返回给客户端,可以使用我们的序列化组件先序列化一下
serializer = StudentModelSerializer(instance=instance)
return Response(serializer.data)
class StudentAPIView(APIView):
def get(self,request,pk):
"""获取单条数据"""
instance = models.Student.objects.get(pk=pk)
serializer = StudentModelSerializer(instance=instance)
return Response(serializer.data)
def put(self,request,pk):
"""更新学生信息"""
#由于更新操作,我们需要找到被更新的是哪条记录,所以需要客户端给我们传一个id过来,并且以后我们会用到获取一条数据,删除一条数据,都需要用到一个id值,所以我们把上面的get和post方法写成一个类,更新、获取单条数据和删除数据我们再单独写一个类
data = request.data
student = models.Student.objects.get(id=pk)
#可能提交过来的是部分数据,所以加上一个partial=True参数,只验证提交过来的数据
serializer = StudentModelSerializer(instance=student,data=data,partial=True)
serializer.is_valid(raise_exception=True)
instance = serializer.save()
# 将修改后的数据返回给客户端
serializer = StudentModelSerializer(instance=instance)
return Response(serializer.data)
def delete(self,request,pk):
"""删除一条数据,用不到我们的序列化器"""
ret = models.Student.objects.get(id=pk).delete()
return Response(None,status=status.HTTP_204_NO_CONTENT)
对应的urls.py
from django.urls import path,re_path
from . import views
urlpatterns = [
path(r'students/',views.StudentsAPIView.as_view()),
re_path(r'students/(?P<pk>d+)/',views.StudentAPIView.as_view()),
]
下面来看一下rest_framework提供的其他功能,如何简化我们的代码
8.1.2 GenericAPIView(通用视图类)
from rest_framework.generics import GenericAPIView
继承自APIView
,主要增加了操作序列化器和数据库查询的方法,作用是为下面Mixin扩展类的执行提供方法支持,通常在使用时,可搭配一个或多个Mixin扩展类
8.1.2.1提供的关于序列化器使用的属性与方法:
-
属性:
- serializer_class指明视图使用的序列化器
-
方法:
- get_serializer_class(self)
当出现一个视图类中调用多个序列化器时,那么可以通过条件判断在get_serializer_class方法中通过返回不同的序列化器类名就可以让视图方法执行不同的序列化器对象了
返回
序列化器类,默认返回serializer_class
def get_serializer_class(self): """重写获取序列化器类的方法""" if self.request.method == "GET": return StudentModel2Serializer else: return StudentModelSerializer
- get_serializer()
返回序列化器对象,主要用来给Mixin扩展类使用,若果我们在视图中想要获取序列化器对象,也可以直接调用此方法
注意:该方法在提供序列化器对象的时候,会想序列化器对象的context属性补充三个数据:request,format,view,这三个数据对象可以在定义序列化器时使用
比如:
serializer = serlf.get_serializer(instance=self.get_object(),context={'pk':pk})
下面的request和view后面会用到
1.request当前视图的请求对象
2.view当前请求的类视图对象
3.format 当前请求期望返回的数据格式
8.1.2.2提供的关于数据库查询的属性与方法
-
属性:
- queryset指明使用的数据查询集
-
方法:
-
get_queryset(self)
返回视图使用的查询集,主要用来提供给Minx扩展类使用,是列表视图与详情视图获取数据的基础,默认返回
queryset
属性,可以重写,例如:def get_queryset(self): user = self.request.user return user.accounts.all()
-
get_object(self)
返回详情视图所需的模型数据对象,主要来提供给Mixin扩展类使用
在视图中可以调用该方法获取详情信息的模型类对象
若详情访问的模型类对象不存在,会返回404
该方法会默认使用APIView提供的check_object_permissions方法检查当前对象是否有权限被访问
# url(r'^books/(?P<pk>d+)/$', views.BookDetailView.as_view()), class BookDetailView(GenericAPIView): queryset = BookInfo.objects.all() serializer_class = BookInfoSerializer def get(self, request, pk): book = self.get_object() # get_object()方法根据pk参数查找queryset中的数据对象 serializer = self.get_serializer(book) return Response(serializer.data)
-
8.1.2.3其他可以设置的属性
- pagination_class指明分页控制类
- filter_backends指明过滤控制后端(查询一些数据的过滤条件等等)
新建一个子应用,来使用来一下GenericAPIView
通用视图类
python manage.py startapp app02
from rest_framework.generics import GenericAPIView
from students.models import Student
from .serializers import StudentModelSerializer, StudentModel2Serializer
from rest_framework.response import Response
class StudentsGenericAPIView(GenericAPIView):
# 本次视图类中要操作的数据[必填]
queryset = Student.objects.all() #基本上是固定的,查询就这么写
# 本次视图类中要调用的默认序列化器[选填]
serializer_class = StudentModelSerializer
def get(self, request):
"""获取所有学生信息"""
serializer = self.get_serializer(instance=self.get_queryset(), many=True)
return Response(serializer.data)
def post(self,request):
data = request.data
serializer = self.get_serializer(data=data)
serializer.is_valid(raise_exception=True)
instance = serializer.save()
serializer = self.get_serializer(instance=instance)
return Response(serializer.data)
class StudentGenericAPIView(GenericAPIView):
queryset = Student.objects.all()
serializer_class = StudentModelSerializer
#一个视图中使用多个序列化类的方法,目前的例子是:get请求获取数据时,我们只给他两个字段数据,其他方法时我们给他所有字段数据,定义了这个get_serializer_class方法之后(其实是对父类的方法进行了重写),其实上面的serializer_class就可以不同写了
def get_serializer_class(self):
"""重写获取序列化器类的方法"""
if self.request.method == "GET":
return StudentModel2Serializer
else:
return StudentModelSerializer
#在使用GenericAPIView实现获取操作单个数据时,我们试图方法中的参数变量pk最好是pk名,别叫id什么的,不然还需要进行一些其他的配置,比较麻烦一些了,简单看一下源码就知道了
def get(self,request,pk):
"""获取一条数据"""
serializer = self.get_serializer(instance=self.get_object())
return Response(serializer.data)
def put(self,request,pk):
data = request.data
serializer = self.get_serializer(instance=self.get_object(),data=data)
serializer.is_valid(raise_exception=True)
serializer.save()
serializer = self.get_serializer(instance=self.get_object())
return Response(serializer.data)
新建一个serializers.py文件里面写序列化器类
from rest_framework import serializers
from app01 import models
class StudentSerializer(serializers.ModelSerializer):
class Meta:
model = models.Student
fields = "__all__"
extra_kwargs = {
'id':{'read_only':True},
'age':{'write_only':True},
}
class Student1Serializer(serializers.ModelSerializer):
class Meta:
model = models.Student
fields = ['name','class_null']
extra_kwargs = {
'id':{'read_only':True},
'age':{'write_only':True}
}
其实GenericaAPIView只是帮我们把数据库查询和调用序列化器类进行了一步封装,目的是将一些公共性质的代码挑出去封装一下,公共性质的代码都不要我们写了,看下面几个扩展类来增强GenericAPIView的作用
8.2 5个视图扩展类
作用:
提供了几种后端视图(对数据资源进行增删改查)处理流程的实现,可以通过继承相应的扩展类来复用代码,减少自己编写的代码量
这5个扩展类需要搭配GenericAPIView父类,因为5个扩展类的实现需要调用GenericAPIView提供的序列化器与数据库查询的方法
8.2.1 ListModelMixin
获取多条数据的列表视图扩展类,提供list(request,*args,**kwargs)
方法快速实现列表视图,返回200状态码(相当于get方法)
该Mixin的list方法会对数据进行过滤和分页
源代码:
class ListModelMixin(object):
"""
List a queryset.
"""
def list(self, request, *args, **kwargs):
# 过滤
queryset = self.filter_queryset(self.get_queryset())
# 分页
page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)
# 序列化
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
举例:
from rest_framework.mixins import ListModelMixin
class BookListView(ListModelMixin, GenericAPIView):
queryset = BookInfo.objects.all()
serializer_class = BookInfoSerializer
def get(self, request):
return self.list(request)
8.2.2 CreateModelMixin
添加数据的创建视图扩展类,提供create(request,*args,**kwargs)
方法快速实现创建资源的视图,成功返回201状态码(相当于post方法)
如果序列化器对前端发送数据验证失败,返回400错误码
源代码:
class CreateModelMixin(object):
"""
Create a model instance.
"""
def create(self, request, *args, **kwargs):
# 获取序列化器
serializer = self.get_serializer(data=request.data)
# 验证
serializer.is_valid(raise_exception=True)
# 保存
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
def perform_create(self, serializer):
serializer.save()
def get_success_headers(self, data):
try:
return {'Location': str(data[api_settings.URL_FIELD_NAME])}
except (TypeError, KeyError):
return {}
示例:
def post(self,request):
return self.create(requset)
8.2.3 RetrieveModelMixin
获取单条数据,详情视图扩展类,提供retrieve(request,*args,**kwargs)
方法,可以快速实现返回一个存在的数据对象(获取单条数据)
如果存在,返回200,不存在返回404
源代码:
class RetrieveModelMixin(object):
"""
Retrieve a model instance.
"""
def retrieve(self, request, *args, **kwargs):
# 获取对象,会检查对象的权限
instance = self.get_object()
# 序列化
serializer = self.get_serializer(instance)
return Response(serializer.data)
举例:
class BookDetailView(RetrieveModelMixin, GenericAPIView):
queryset = BookInfo.objects.all()
serializer_class = BookInfoSerializer
def get(self, request, pk):
return self.retrieve(request,pk)
return self.retrieve(request) #pk也可以不传
8.2.4 UpdateModelMixin
更新视图扩展类,提供update(request,*args,**kwargs)
方法,可以快速实现更新一个存在的数据对象
同时也提供partial_update(request,*args,**kwargs)
方法,可以实现局部更新
成功返回200,序列化器校验数据失败时,返回400错误
源代码:
class UpdateModelMixin(object):
"""
Update a model instance.
"""
def update(self, request, *args, **kwargs):
partial = kwargs.pop('partial', False)
instance = self.get_object()
serializer = self.get_serializer(instance, data=request.data, partial=partial)
serializer.is_valid(raise_exception=True)
self.perform_update(serializer)
if getattr(instance, '_prefetched_objects_cache', None):
# If 'prefetch_related' has been applied to a queryset, we need to
# forcibly invalidate the prefetch cache on the instance.
instance._prefetched_objects_cache = {}
return Response(serializer.data)
def perform_update(self, serializer):
serializer.save()
def partial_update(self, request, *args, **kwargs):
kwargs['partial'] = True
return self.update(request, *args, **kwargs)
示例:
def put(self,request,pk):
"""更新一条数据"""
return self.update(request,pk)
8.2.5 DestroyModelMixin
删除视图扩展类,提供destroy(request,*args,**kwargs)
方法,可以快速实现删除一个存在的数据对象
成功返回204,不存在返回404
源代码:
class DestroyModelMixin(object):
"""
Destroy a model instance.
"""
def destroy(self, request, *args, **kwargs):
instance = self.get_object()
self.perform_destroy(instance)
return Response(status=status.HTTP_204_NO_CONTENT)
def perform_destroy(self, instance):
instance.delete()
示例:
def delete(self,request,pk):
"""删除一条数据"""
return self.destroy(request,pk)
使用GenericAPIView和视图扩展类,实现api接口,代码:
"""GenericAPIView结合视图扩展类实现api接口"""
from rest_framework.mixins import ListModelMixin,CreateModelMixin
class Students2GenericAPIView(GenericAPIView,ListModelMixin,CreateModelMixin):
# 本次视图类中要操作的数据[必填]
queryset = Student.objects.all()
# 本次视图类中要调用的默认序列化器[选填]
serializer_class = StudentModelSerializer
def get(self, request):
"""获取多个学生信息"""
return self.list(request)
def post(self,request):
"""添加学生信息"""
return self.create(request)
from rest_framework.mixins import RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin
class Student2GenericAPIView(GenericAPIView,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin):
queryset = Student.objects.all()
serializer_class = StudentModelSerializer
# 在使用GenericAPIView视图获取或操作单个数据时,视图方法中的代表主键的参数最好是pk
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)
8.3 GenericAPIView的视图子类
8.3.1 CreateAPIView
提供 post 方法
继承自: GenericAPIView、CreateModelMixin
8.3.2 ListAPIView
提供 get 方法
继承自:GenericAPIView、ListModelMixin
8.3.3 RetrieveAPIView
提供 get 方法
继承自: GenericAPIView、RetrieveModelMixin
8.3.4 DestoryAPIView
提供 delete 方法
继承自:GenericAPIView、DestoryModelMixin
8.3.5 UpdateAPIView
提供 put 和 patch 方法
继承自:GenericAPIView、UpdateModelMixin
8.3.6 RetrieveUpdateAPIView
提供 get、put、patch方法
继承自: GenericAPIView、RetrieveModelMixin、UpdateModelMixin
8.3.7 RetrieveUpdateDestoryAPIView
提供 get、put、patch、delete方法
继承自:GenericAPIView、RetrieveModelMixin、UpdateModelMixin、DestoryModelMixin
简化代码:
############### 基于视图子类的接口 #########################################
from rest_framework.generics import ListAPIView,CreateAPIView,RetrieveAPIView,UpdateAPIView,DestroyAPIView
class Students4View(ListAPIView,CreateAPIView):
queryset = models.Student.objects.all()
serializer_class = StudentSerializer
class Student4View(RetrieveAPIView,UpdateAPIView,DestroyAPIView):
queryset = models.Student.objects.all()
serializer_class = StudentSerializer
8.4 视图集基类
8.4.1 ViewSet
继承自APIView
与ViewSetMixin
作用也与APIView
基本类似提供类身份认证,权限校验流量管理等
ViewSet主要通过继承ViewSetMixin来实现在调用as_view()时传入字典如{'get':'list'}的映射工作
在ViewSet中,没有提供任何action方法,需要我们自己实现action方法
使用视图集ViewSet,可以将一系列逻辑相关的动作放到一个类中:
- list() 提供一组数据
- retrieve() 提供单个数据
- create() 创建数据
- update() 保存数据
- destory() 删除数据
代码:
from rest_framework.viewsets import ViewSet,GenericViewSet
class Students5View(ViewSet):
# 获取所有数据接口
def get_all_student(self,request):
all_data = models.Student.objects.all()
serializer = StudentSerializer(instance=all_data,many=True)
return Response(serializer.data)
# 添加一条记录的接口
def add_student(self,request):
data = request.data
serializer = StudentSerializer(data=data)
if serializer.is_valid():
instance = serializer.save()
serializer = StudentSerializer(instance=instance)
return Response(serializer.data,status=status.HTTP_201_CREATED)
else:
return Response({'msg':'有错误'})
# 获取单条数据
def get_one(self,request,pk):
stu_obj = models.Student.objects.get(pk=pk)
serializer = StudentSerializer(instance=stu_obj)
return Response(serializer.data)
urls.py(路由设置)
from django.urls import path,re_path
from app01 import views
urlpatterns = [
path('students5/', views.Students5View.as_view({'get':'get_all_student','post':'add_student'})),
re_path('students5/(?P<pk>d+)/', views.Students5View.as_view({'get':'get_one'})),
]
改善代码:
"""发挥下ViewSet的作用"""
from rest_framework.viewsets import ViewSet
from rest_framework.generics import ListAPIView,CreateAPIView,RetrieveAPIView
class Student2ViewSet(ViewSet,ListAPIView,CreateAPIView,RetrieveAPIView):
queryset = 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)
8.4.2 GenericViewSet
使用ViewSet通常并不方便,因为list、retrieve、create、update、destory等方法都需要自己编写,而这些方法与前面讲过的Mixin扩展类提供的方法同名,所以我们可以通过继承Mixin扩展类来复用这些方法而无需自己编写。但是Mixin扩展类依赖于GenericAPIView
,所以还需要继承GenericAPIView
。
GenericViewSet就帮助我们完成了这样的继承工作,继承自GenericAPIView
与ViewSetMixin
,在实现了调用as_view()时传入字典(如{'get':'list'}
)的映射处理工作的同时,还提供了GenericAPIView
提供的基础方法,可以直接搭配Mixin扩展类使用。
举例:
from rest_framework.viewsets import ViewSet,GenericViewSet
from rest_framework.mixins import ListModelMixin,CreateModelMixin,UpdateModelMixin,RetrieveModelMixin,DestroyModelMixin
class Students7View(GenericViewSet,ListModelMixin,CreateModelMixin,UpdateModelMixin,RetrieveModelMixin,DestroyModelMixin):
queryset = models.Student.objects.all()
serializer_class = StudentSerializer
# 获取所有数据接口
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)
urls.py
path('students7/', views.Students7View.as_view({'get':'get_all_student','post':'add_student'})),
re_path('students7/(?P<pk>d+)/', views.Students7View.as_view({'get':'get_one','put':'updat_one','delete':'delete_one'})),
简化代码:
from rest_framework.viewsets import ViewSet,GenericViewSet
from rest_framework.mixins import ListModelMixin,CreateModelMixin,UpdateModelMixin,RetrieveModelMixin,DestroyModelMixin
class Students7View(GenericViewSet,ListModelMixin,CreateModelMixin,UpdateModelMixin,RetrieveModelMixin,DestroyModelMixin):
queryset = models.Student.objects.all()
serializer_class = StudentSerializer
urls.py
path('students7/', views.Students7View.as_view({'get':'list','post':'create'})),
re_path('students7/(?P<pk>d+)/', views.Students7View.as_view({'get':'retrieve','put':'update','delete':'destroy'})),
8.4.3 ModelViewSet
继承GenericViewSet
,同时包括了ListModelMixin、RetrieveModelMixin、CreateModelMixin、UpdateModelMixin、DestoryModelMixin。
示例:
"""使用ModelViewSet简写上面的继承代码,最终版"""
from rest_framework.decorators import action
from rest_framework.viewsets import ModelViewSet
class Students8View(ModelViewSet):
queryset = models.Student.objects.all()
serializer_class = StudentSerializer
urls.py
path('students8/', views.Students8View.as_view({'get':'list','post':'create'})),
re_path('students8/(?P<pk>d+)/', views.Students8View.as_view({'get':'retrieve','put':'update','delete':'destroy'})),
总结:
继承视图集父类必须在url中.as_view()中配置action,如{'get':'list'}
8.4.4 视图集中定义附加action动作
在视图集中,除了上述默认的方法动作外,还可以添加自定义动作,进行扩展
举例,比如做一个登录方法login:
from rest_framework.viewsets import ModelViewSet,ReadOnlyModelViewSet
class StudentModelViewSet(ModelViewSet):
queryset = Student.objects.all()
serializer_class = StudentModelSerializer
def login(self,request): #这个就可以称为自定义的action动作
"""学生登录功能"""
return Response({"message":"登录成功"})
ulrs.py
urlpatterns = [
path("students8/", views.StudentModelViewSet.as_view({"get": "list", "post": "create"})),
re_path("students8/(?P<pk>d+)/",
views.StudentModelViewSet.as_view({"get": "retrieve", "put": "update", "delete": "destroy"})),
path("students8/login/",views.StudentModelViewSet.as_view({"get":"login"}))
]
8.4.5 action属性
在视图集中,我们可以通过action对象属性来获取当前请求视图集时的action动作是哪个
举例:
from rest_framework.viewsets import ModelViewSet
from students.models import Student
from .serializers import StudentModelSerializer
from rest_framework.response import Response
class StudentModelViewSet(ModelViewSet):
queryset = Student.objects.all()
serializer_class = StudentModelSerializer
def get_new_5(self,request):
"""获取最近添加的5个学生信息"""
# 操作数据库
print(self.action) # 获取本次请求的视图方法名
通过路由访问到当前方法中.可以看到本次的action就是请求的方法名
9.路由Routers
对于视图集ViewSet,我们除了可以自己手动指明请求方式与动作action之间的对应关系;外,还可以使用Routers来帮助我们快速实现路由信息
REST framework提供了两个router
- SimpleRouter
- DefaultRouter
9.1 使用方法
1) 创建router对象,并注册视图集
from rest_framework import routers
router = routers.DefaultRouter()
router.register(r'router_stu', StudentModelViewSet, base_name='student')
get login
#但是注意:路由对象只会按照modelviewset中的五个接口生成对应的访问地址而已,上面的login这种自定义动作就不能访问到了
register(prefix,viewset,base_name)
- prfix该视图集的路由前缀
- viewset视图集
- base_name路由别名的前缀
2) 添加路由数据
方式一(简单常用)
urlpatterns = [
...
]
urlpatterns += router.urls
方式二
urlpatterns = [
...
url(r'^', include(router.urls))
]
使用路由类给视图集生成了路由地址
from rest_framework.viewsets import ModelViewSet,ReadOnlyModelViewSet
class StudentModelViewSet(ModelViewSet):
queryset = Student.objects.all()
serializer_class = StudentModelSerializer
def login(self,request):
"""学生登录功能"""
print(self.action)
return Response({"message":"登录成功"})
路由代码:
from django.urls import path, re_path
from . import views
urlpatterns = [
...
]
"""使用drf提供路由类router给视图集生成路由列表"""
# 实例化路由类
# drf提供一共提供了两个路由类给我们使用,他们用法一致,功能几乎一样
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
# 注册视图集
# router.register("路由前缀",视图集类)
router.register("router_stu",views.StudentModelViewSet)
# 把生成的路由列表追加到urlpatterns
print( router.urls )
urlpatterns += router.urls
上面的代码只是生成了路由地址[增/删/改/查一条/查多条的功能],但是不会自动帮我们生成在视图集中自定义方法的路由,要给自定义方法生成路由,需要进行action动作的声明
9.2 视图集中附加action声明
在视图集中使用rest_framework.decorators.action
装饰器
以action装饰器装饰的方法名会作为action动作名,与list,retrieve等同
action装饰器可以接受两个参数:
-
methods声明该action对应的请求方式,列表传递
-
detail:声明该action的路径是否与单一资源对应
xxx/<pk>/action方法名/
- True 表示路径格式是
xxx/<pk>/action方法名/
- False表示路径格式是
xxx/action方法名/
- True 表示路径格式是
举例:
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个学生信息"""
...
小结:
DefaultRouter与SimpleRouter的区别是,DefaultRouter会多附带一个默认的API根视图,返回一个包含所有列表视图的超链接响应数据,比如使用DefaultRouter是访问一下`http://127.0.0.1:8001/router_stu/`会看到一个页面,而SimpleRouter会报错,一般都需要有个查看所有接口的页面,所以基本用的都是DefaultRouter
DRF框架的扩展功能
使用django自带的admin认证
创建一个新的应用
python manage.py startapp app02
因为接下来的功能需要使用到登录功能,所以我们使用django内置admin站点并创建一个管理员
python manage.py createsuperuser
填一下用户名,邮箱和密码
root
2468688808@qq.com
创建管理员后,访问admin站点,先修改站点的语言配置settings.py
LANGUAGE_CODE = 'zh-hans'
from rest_framework import settings
在admin.py文件中,简单使用
from django.contrib import admin
# Register your models here.
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)
1. 认证Authentication
可以在配置文件中配置全局默认的认证方案
from rest_framework import settings # 可以看到默认使用的
# 在settings配置文件中,我们可以进行下面的配置来覆盖默认配置
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES':(
# 哪个写在前面,优先使用哪个认证
'rest_framework.authentication.SessionAuthentication',
# session认证,admin后台其实就使用的session认证,其实接口开发很少用到session认证,所以我们通过配置可以改为其他认证,比如后面项目里用到的jwt,JSON,WEB,TOKEN认证或者一些配合redis的认证
'rest_framework.authentication.BasicAuthentication',
# 基本认证, 工作当中可能一些测试人员参与的话,会将一些认证数据保存在内存中,然后验证,开发用不上
)
}
也可以在每个视图中通过设置authentication_classes属性来设置,比如说我们很多接口的数据都是可以让别人获取数据的,但是有可能有些接口是调用给别人网站的,有可能就需要我们做一些单独的认证了。
from rest_framework.authentication import SessionAuthentication, BasicAuthentication
from rest_framework.views import APIView
class ExampleView(APIView):
# 类属性
authentication_classes = [SessionAuthentication, BasicAuthentication] #也可以写成元祖形式的,到时候我们使用我们自己开发的认证组件的时候,就需要自己写一个认证组件类,然后写在列表中来使用
...
认证失败会有两种可能的返回值:
- 401 Unauthorized未认证
- 403 Permission Denied权限被禁止
示例1: 自定义认证组件
- 写一个认证类
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
class Auth(BaseAuthentication):
def authenticate(self, request):
# request.POST.get('username')
username = 'root'
if username == 'root':
return 'yunchao','oo' # request.user = 'root' , request.auth ='oo'
else:
return AuthenticationFailed('denglu renzheng shibai')
全局使用就在settings配置文件中使用
REST_FRAMEWORK = {
# 'DEFAULT_RENDERER_CLASSES': [
# 'rest_framework.renderers.JSONRenderer',
# 'rest_framework.renderers.BrowsableAPIRenderer',
# ],
'DEFAULT_AUTHENTICATION_CLASSES': (
#'app01.utils.auth.Auth', # 全局配置,所有的接口请求过来,都会执行这个类
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.BasicAuthentication'
),
}
局部视图中可以给单独的数据接口配置认证
from rest_framework.authentication import SessionAuthentication, BasicAuthentication
from app01.utils.auth import Auth
class Students1View(APIView):
authentication_classes = [Auth,]
# 获取所有数据借口
def get(self,request):
print(request.user)
print(request.auth)
all_data = models.Student.objects.all()
serializer = StudentSerializer(instance=all_data,many=True)
return Response(serializer.data)
# 添加一条记录的接口
def post(self,request):
data = request.data
serializer = StudentSerializer(data=data)
if serializer.is_valid():
instance = serializer.save()
serializer = StudentSerializer(instance=instance)
return Response(serializer.data,status=status.HTTP_201_CREATED)
else:
return Response({'xxx':'有字段错误'})
class UserAuth():
def authenticate_header(self,request):
pass
#authenticate方法固定的,并且必须有个参数,这个参数是新的request对象,不信,看源码
def authenticate(self,request):
print('搞事情')
if 1:
#源码中会发现,这个方法会有两个返回值,并且这两个返回值封装到了新的request对象中了,request.user-->用户名 和 request.auth-->token值,这两个值作为认证结束后的返回结果
return "chao","asdfasdfasdf"
class BookView(APIView):
#认证组件肯定是在get、post等方法执行之前执行的,还记得源码的地方吗,这个组件是在dispatch的地方调用的,我们是上面写个UserAuth类
authentication_classes = [UserAuth,] #认证类可以写多个,一个一个的顺序验证
def get(self,request):
'''
查看所有书籍
:param request:
:return:
'''
#这样就拿到了上面UserAuth类的authenticate方法的两个返回值
print(request.user)
print(request.auth)
book_obj_list = models.Book.objects.all()
s_books = BookSerializers(book_obj_list,many=True)
return Response(s_books.data)
2. 权限Permissions
权限控制可以限制用户对于视图的访问和对于具体数据对象的访问
- 在执行视图的dispatch()方法之前,会先进行视图访问权限的判断
- 在通过get_obj()获取具体对象时,会进行模型对象访问权限的判断
使用:
可以在配置文件中全局设置默认的权限管理类
REST_FRAMEWORK = {
....
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated', #登录状态下才能访问我们的接口,退出admin后台之后,就访问不了了
)
}
如果没有特别指明,采用的是默认配置
from rest_framework import permissions
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.AllowAny', #表示任何人都可以进行任何的操作,没做限制
)
也可以在具体的视图中通过permission_classes属性来设置
from rest_framework.permissions import IsAuthenticated
from rest_framework.views import APIView
class ExampleView(APIView):
permission_classes = (IsAuthenticated,)
...
提供的权限
- AllowAny 允许所有用户
- IsAuthenticated 仅通过认真的用户
- IsAdminUser 仅管理员用户
- IsAuthenticatedOrReadOnly 已经登录认证的用户可以对数据进行增删改查操作,没有登录认证的只能查看数据
2.1 自定义权限
如需自定义权限,需继承rest_framework.permissions.BasePermission父类,并实现以下两个任何一个方法或全部
-
has_permission(self,request,view)
是否可以访问视图,view表示当前视图对象
-
has_object_permission(self,request,view,obj)
是否可以访问数据对象,view表示当前视图,obj为数据对象
例如:
在当前子应用下,创建一个权限文件permissions.py中声明自定义权限类
from rest_framework.permissions import BasePermission
class MyPermission(BasePermission):
def has_permission(self,request,view):
print(request,view)
print(request.user) # 未登录,AnonymousUser,匿名用户
if (request.user.type == 'svip'):
return True
else:
return False
局部配置权限
from rest_framework.permissions import IsAuthenticated
from app01.utils.auth import Auth,MyPermission
class Students1View(APIView):
# permission_classes = [IsAuthenticated]
# permission_classes = [MyPermission]
# authentication_classes = [Auth,]
全局配置权限
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': [
'app01.utils.auth.MyPermission' # 全局配置
# 'rest_framework.permissions.IsAuthenticated',
],
}
3. 限流Throttling
可以对接口访问的频次进行限制,以减轻服务器压力
一般用于付费购买次数,投票等场景使用
3.1使用
可以在配置文件中,使用DEFAULT_THROTTLE_CLASSES
和DEFAULT_THROTTLE_RATES
进行全局配置
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_CLASSES': (
'rest_framework.throttling.AnonRateThrottle', #匿名用户,未登录的
'rest_framework.throttling.UserRateThrottle' #经过登录之后的用户
),
'DEFAULT_THROTTLE_RATES': {
'anon': '100/day',
'user': '1000/day'
}
}
DEFAULT_THROTTLE_RATES
可以使用second
,minute
,hour
或day
来指明周期
源码:
{'s': 1, 'm': 60, 'h': 3600, 'd': 86400} m表示分钟,可以写m,也可以写minute
也可以在具体视图中通过throttle_classes
属性来配置
from rest_framework.throttling import UserRateThrottle
from rest_framework.views import APIView
class ExampleView(APIView):
throttle_classes = (UserRateThrottle,) #局部配置
...
3.2 可选限流类
-
AnonRateThrottle
限制所有匿名未认证用户,使用IP区分用户
-
UserRateThrottle
限制认证用户,使用User id来区分
使用
DEFAULT_THROTTLE_RATES['user']
来设置频次 -
ScopedRateThrottle(待定)
限制用户对于每个视图的访问频次,使用ip或user id,先找用户id,没有设置用户id的话就会使用ip地址
全局配置
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_CLASSES': (
'rest_framework.throttling.AnonRateThrottle', #匿名用户,未登录的
'rest_framework.throttling.UserRateThrottle' #经过登录之后的用户
),
'DEFAULT_THROTTLE_RATES': {
'anon': '100/day',
'user': '1000/day'
}
}
局部配置
REST_FRAMEWORK = {
#'DEFAULT_THROTTLE_CLASSES': (
# 'rest_framework.throttling.AnonRateThrottle', #匿名用户,未登录的
# 'rest_framework.throttling.UserRateThrottle' #经过登录之后的用户
#),
'DEFAULT_THROTTLE_RATES': {
'anon': '100/day',
'user': '1000/day'
}
}
views.py
from rest_framework.throttling import UserRateThrottle
class Students1View(APIView):
throttle_classes = [UserRateThrottle]
4. 过滤Filtering
对于列表数据可能需要根据字段进行过滤,我们可以通过添加django-filter
扩展
pip3 install django-filter
# 会自动把django升级,记得
pip3 uninstall django
pip3 install django==2.2
在配置文件中添加过滤后端的设置
INSTALLED_APPS = [
...
'django_filters', # 需要注册应用,
]
REST_FRAMEWORK = {
...
'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',)
}
在视图中添加filter_fields
属性,指定可以过滤的字段
class Students8View(ModelViewSet):
queryset = models.Student.objects.all()
serializer_class = StudentSerializer
filter_fields = ('name','age')
# 127.0.0.1:8000/four/students/?name=
5. 排序
对于列表数据,Rest framework提供了 OrderingFilter过滤器来帮助我们快速指明数据按照指定字段进行排序
5.1使用方法:
在类视图中设置filter_backends
,使用rest_framework.filters.OrderingFilter
过滤器,Rest framework会在请求的查询字符串参数中检查是否包含了ordering参数,如果包含了ordering参数,则按照ordering参数指明的排序字段对数据进行排序。
前端可以传递的ordering参数的可选字段值需要在ordering_fields
中指明。
示例:
from rest_framework.filters import OrderingFilter
from django_filters.rest_framework import DjangoFilterBackend# 需要使用一下他才能让过滤和排序结合使用
class Students8View(ModelViewSet):
queryset = models.Student.objects.all()
serializer_class = StudentSerializer
filter_backends = [OrderingFilter,DjangoFilterBackend]# 因为filter_backends是局部过滤配置,局部配置会覆盖全局配置,所以需要重新把过滤组件核心类再次声明 否则过滤功能会失效
filter_fields = ('name','age')
ordering_fields = ('id','age')
# 针对的是继承的类中的list方法
# http://127.0.0.1:8000/app01/dd/?age=18&ordering=-id
6. 分页
settings.py中设置全局的分页方式
REST_FRAMEWORK = {
# 全局分页,一旦设置了全局分页,那么我们drf中的视图扩展类里面的list方法提供的列表页都会产生分页的效果。所以一般不用全局分页
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 100 # 每页最大数据量
}
也可以通过自定义Pagination类,来为视图添加不同的分页行为,在视图中通过pagination_class
属性来指明
class LargeResultsSetPagination(PageNumberPagination):
page_size = 1000 #每页显示多少条
#127.0.0.1:8001/students/?page=5&page_size=10
page_size_query_param = 'page_size'
max_page_size = 10000
class BookDetailView(RetrieveAPIView):
queryset = BookInfo.objects.all()
serializer_class = BookInfoSerializer
pagination_class = LargeResultsSetPagination
注意: 如果在视图内关闭分页功能,只需在视图内设置
pagination_class = None
6.1 可选分页器
6.1.1 PageNumberPagination
可以在子类中定义的属性"
- page_size 每页显示的数量
- page_query_param 客户端指定分页的关键字,默认为page
- page_size_query_param 客户端自定义获取每页条数的关键字
- max_page_size 客户端最多每页能获取的最大条数
示例:
from rest_framework.pagination import PageNumberPagination,LimitOffsetPagination
class LargeResultsSetPagination(PageNumberPagination):
page_size = 2 # 每页显示多少条
# 127.0.0.1:8001/students/?page=5&page_size=10
page_query_param = 'pp' # 指定分页的关键字,默认为page
page_size_query_param = 'size'# 客户端可以通过 size=10 来获取 10条数据
max_page_size = 3 # 客户端最大获取的数量
#http://127.0.0.1:8000/app01/dd/?pp=1&size=5
from rest_framework.viewsets import ModelViewSet
from rest_framework.filters import OrderingFilter
from django_filters.rest_framework import DjangoFilterBackend
class Students8View(ModelViewSet):
queryset = models.Student.objects.all()
serializer_class = StudentSerializer
filter_backends = [OrderingFilter,DjangoFilterBackend]
filter_fields = ('name','age')
ordering_fields = ('id','age')
#pagination_class = LargeResultsSetPagination
pagination_class = LargePagination
6.1.2 LimitOffsetPagination
还可以通过偏移量来取数据
可以在子类中定义的属性:
- default_limit 默认限制,每页数据量大小,默认值与
PAGE_SIZE
设置一致 - limit_query_param limit参数名,默认'limit' , 可以通过这个参数来改名字
- offset_query_param offset参数名,默认'offset' ,可以通过这个参数来改名字
- max_limit 最大limit限制,默认None, 无限制
from rest_framework.pagination import PageNumberPagination,LimitOffsetPagination
class LargePagination(LimitOffsetPagination):
default_limit = 2
limit_query_param = "size" # 默认是limit
offset_query_param = "start" # 默认是offset
from rest_framework.viewsets import ModelViewSet
from rest_framework.filters import OrderingFilter
from django_filters.rest_framework import DjangoFilterBackend
class Students8View(ModelViewSet):
queryset = models.Student.objects.all()
serializer_class = StudentSerializer
filter_backends = [OrderingFilter,DjangoFilterBackend]
filter_fields = ('name','age')
ordering_fields = ('id','age')
#pagination_class = LargeResultsSetPagination
pagination_class = LargePagination
7. 异常处理 Exceptions
7.1 自定义异常错误
Rest framework提供了异常处理,我们可以自定义异常处理函数
创建一个exceptions文件
from rest_framework.views import exception_handler
from app01.views import APIError
from rest_framework.response import Response
def myexception_handler(exc,context):
# exc 错误对象
# context 异常发生时的一些上下文信息
# 先调用rest framework默认的异常处理方法获得标准错误响应对象
ret = exception_handler(exc,context)# 这个函数是drf提供的,他处理了一些错误,但是如果它处理不了的,会返回None,所以,如果是None的话,我们需要自己来处理错误
# print(exc,type(exc))
if ret is None:
if isinstance(exc,APIError):
return Response('api错误')
else:
return ret
views.py
class APIError(Exception):
pass
class Test(APIView):
def get(self,request):
try:
models.Student.objects.get(pk=100)
except models.Student.DoesNotExist:
raise APIError('错啦错啦')
return Response({'msg':'查询出错'})
return Response({'msg':'xxxxx'})
settings.py,声明自定义的异常处理
REST_FRAMEWORK = {
'EXCEPTION_HANDLER': 'app01.myexceptions.myexception_handler'
# 如果未声明会采用默认的方式
#'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler'
}
urls.py
urlpatterns = [
path('test/', views.Test.as_view()),
]
补充处理关于数据库的异常
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
7.2 rest framework定义的异常
- APIException 所有的异常的父类
- ParseError 解析错误
- AuthenticationFailed 认证失败
- NotAuthenticated 尚未认证
- PermissionDenied 权限决绝
- NotFound 未找到
- MethodNotAllowed 请求方式不支持
- NotAcceptable 要获取的数据格式不支持
- Throttled 超过限流次数
- ValidationError 校验失败
上面列处的异常不需要我们自行处理了,很多上面没有列出来的异常需要我们在自定义异常中自己处理
8. 自动生成接口文档
rest framework可以自动帮我们生成接口文档
接口文档以网页的方式呈现
自动接口文档能生成是继承自APIView
及其子类的视图
8.1 安装依赖
rest framework生成接口文档需要coreapi
库的支持
pip3 install coreapi
8.2 设置接口文档访问的路径
在总路由中添加接口文档路径
文档路由对应的视图配置为rest_framework.documentation.include_docs_urls
,
参数title
为接口文档网站的标题
from rest_framework.documentation import include_docs_urls
urlpatterns = [
...
path('docs/', include_docs_urls(title='站点页面标题'))
]
settings.py配置
REST_FRAMEWORK = {
...
'DEFAULT_SCHEMA_CLASS': "rest_framework.schemas.AutoSchema",
}
8.3 访问接口文档网页
浏览器访问 127.0.0.1:8000/docs/,即可看到自动生成的接口文档
两点说明:
- 视图集ViewSet的retrieve名称,在接口文档网站中叫做read
- 参数的description需要在模型类或序列化器类的字段中以help_text选项定义
class Student(models.Model):
...
age = models.IntegerField(default=0, verbose_name='年龄', help_text='年龄')
...
9. Xadmin
xadmin是Django的第三方扩展插件,比使用Django的admin站点更强大,也更方便
文档:https://xadmin.readthedocs.io/en/latest/index.html
GitHub地址:https://github.com/sshwsfc/django-xadmin
9.1 安装
pip3 install https://codeload.github.com/sshwsfc/xadmin/zip/django2
在settings.py配置文件中注册如下应用:
INSTALLED_APPS = [
...
'xadmin',
'crispy_forms',
'reversion',
...
]
# 修改使用中文界面
LANGUAGE_CODE = 'zh-Hans'
# 修改时区
TIME_ZONE = 'Asia/Shanghai'
xadmin有建立自己的数据库模型类,需要进行数据库迁移
python manage.py makemigrations
python manage.py migrate
在总路由中添加xadmin的路由信息
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
9.2 使用
- xadmin不在使用Django的admin.py,而是需要编写代码在adminx.py文件中,每一个应用都可以创建一个adminx.py对xadmin站点进行配置
- xadmin的站点管理类不继承
admin.ModelAdmin
.而是直接继承object
即可
在子应用中创建adminx.py文件
9.3站点的全局配置
import xadmin
from xadmin import views
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)
9.4 站点Model管理
xadmin可以使用的页面样式控制基本与Django原生的admin一样
可以在models类中定义__str__
方法来定义对象显示成什么内容
-
list_display控制列表展示的字段
list_play = list_display = ['id','title','link','image_url']
-
search_fields控制可以通过搜索框搜索的字段名称,xadmin使用的是模糊查询
search_fields = ['id','title']
-
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',]
-
这并不是所有功能,可以参看它的文档,它提供的一些功能我们可能还需要自定制,调整或者添加一些它没有的功能,后面再说