DRF
DRF
DRF(Django Rest Framework)是可以快速基于Restful开发的Django应用的插件,功能非常多。
安装
pip install djangorestframework
Django需要2.2
注册
settings.py中增加
INSTALLED_APPS = [
...
'rest_framework',
]
注册他,可以使用它提供的网页。也可以不注册,使用PostMan调试
序列化器
采用前后端分离后,前端与后端交互实际上使用的只是JSON数据
- 序列化:后端查数据库获得模型类,发给前端的数据就是JSON字符串,核心就是如何把实例序列化成JSON发给浏览器
- 实例转换为字典,如何做,使用序列化器
- 字典转换为JSON字符串,使用json模块的dumps
- 反序列化:前端浏览器发请求报文到 后端,封装为request请求对象,提交的数据是JSON字符串,需要反序列化
- JSON字符串转换为字典谁来做? json.loads
- 字典=>?
序列化器类
DRF中序列化器的作用
- 将实例序列化为字典
- 反序列化后得到字典,交给序列化器进行数据校验
参考:
https://www.django-rest-framework.org/api-guide/serializers/
BaseSerializer 类源码分析
class BaseSerializer(Field):
def __init__(self, instance=None, data=empty, **kwargs):
self.instance = instance # 第一个参数表示实例
if data is not empty:
self.initial_data = data # 浏览器端提交的字典
self.partial = kwargs.pop('partial', False)
self._context = kwargs.pop('context', {})
kwargs.pop('many', None) # many表示数据是多个还是一个
super().__init__(**kwargs)
def is_valid(self, raise_exception=False):
"""反序列化校验数据时调用,也就是对浏览器端提交的数据进行验证"""
@property
def data(self):
if hasattr(self, 'initial_data') and not hasattr(self, '_validated_data'):
# 将浏览器提交数据转换为字典交给序列化器,但没有校验过数据,应先调用is_valid()校验
msg = (
'When a serializer is passed a `data` keyword argument you '
'must call `.is_valid()` before attempting to access the '
'serialized `.data` representation.\n'
'You should either call `.is_valid()` first, '
'or access `.initial_data` instead.'
)
raise AssertionError(msg)
if not hasattr(self, '_data'): # 没有_data立即开始计算
if self.instance is not None and not getattr(self, '_errors', None):
# 序列化。有实例,没有错误
self._data = self.to_representation(self.instance)
elif hasattr(self, '_validated_data') and not getattr(self, '_errors', None):
# 反序列化。is_valid()校验后的数据(提交的数据被验证过了)且无错误
self._data = self.to_representation(self.validated_data)
else:
self._data = self.get_initial()
return self._data
@property
def validated_data(self):
"""获取校验后的数据"""
class Serializer(BaseSerializer, metaclass=SerializerMetaclass):
@property
def data(self):
ret = super().data
return ReturnDict(ret, serializer=self) # OrderedDict
序列化器原理
Model类
from django.db import models
# Create your models here.
class Employees(models.Model):
class Gender(models.IntegerChoices): #枚举类型,限定取值范围
MAN = 1,"男"
WOMEN = 2,"女"
class Meta:
db_table = "employees"
emp_no = models.IntegerField(primary_key=True)
birth_date = models.DateField()
first_name = models.CharField(max_length=14)
last_name = models.CharField(max_length=16)
gender = models.SmallIntegerField(choices=Gender.choices)
hire_date = models.DateField()
@property
def name(self):
return "<{}{}>".format(self.first_name,self.last_name)
def __str__(self):
return "{},{},{}".format(self.emp_no,self.first_name,self.last_name)
__repr__ = __str__
序列化器
在应用目录下构建serializers.py,根据Model类Employee编写EmpSerializer
需要什么字段就在EmpSerializer中定义什么属性
from rest_framework import serializers
from .models import Employees
class EmployeeSerializer(serializers.Serializer):
emp_no = serializers.IntegerField()
birth_date = serializers.DateField()
first_name = serializers.CharField(max_length=14) # max_length反序列化验证用
last_name = serializers.CharField(max_length=16)
gender = serializers.ChoiceField(Employees.Gender.choices)
hire_date = serializers.DateField()
序列化.py
实例转换为字典的过程
import os
import django
from django.core.wsgi import get_wsgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'salary.settings')
django.setup(set_prefix=False)
#序列化
from employee.serializers import Employees,EmployeeSerializer
emp = Employees.objects
empr = emp.get(pk='10010')
print(type(empr),empr)
# 1 单个实例传给构造器的第一参数instance
ser = EmployeeSerializer(instance=empr)
data = ser.data ## 序列化
print(ser.data) # ReturnDict
# 2 查询集多个实例,使用many参数
empr2 = emp.filter(pk__gt='10019')
ser2 = EmployeeSerializer(instance=empr2,many=True)
print(ser2.data,type(ser2.data)) #ReturnList列表套字典
#输出
<class 'employee.models.Employees'> 10010,Duangkaew,Piveteau
{'emp_no': 10010, 'birth_date': '1963-06-01', 'first_name': 'Duangkaew', 'last_name': 'Piveteau', 'gender': 2, 'hire_date': '1989-08-24'}
[OrderedDict([('emp_no', 10020), ('birth_date', '1952-12-24'), ('first_name', 'Mayuko'), ('last_name', 'Warwick'), ('gender', 1), ('hire_date', '1991-01-26')])] <class 'rest_framework.utils.serializer_helpers.ReturnList'>
- 单实例,Employee Instance=>dict
- 多实例,many=True ,QuerySet=>list ,列表中每一个Employee Instance =>dict
反序列化
字典中数据检验,为入库做准备
# 浏览器POST数据为JSON字符串,被DRF request进一步封装成dict
dict = {'emp_no': 10010,
'birth_date': '1963-06-01',
'first_name': 'Duangkaew',
'last_name': 'Piveteau',
'gender': '2', # 这里故意写成字符串
'hire_date': '1989-08-24'} #注意,这是字典数据是data,而不是Employee的实例
#反序列化
serializer = EmployeeSerializer(data=dict) #注意参数不是instance,而是data
#print(serializer.data) 没有调用is_valid()直接报错,客户端提交数据不可信,第一步需要先验证
#is_valid方法调用内部成功会把数据放在_validated_data属性
#raise_exception验证失败是否抛异常
serializer.is_valid(raise_exception=False)
data = serializer.data # 这个data是返回给客户端的数据
print(data,type(data))
validate_data = serializer.validated_data #这个是校验后准备入库的数据
print(validate_data)
#输出
{'emp_no': 10010, 'birth_date': '1963-06-01', 'first_name': 'Duangkaew', 'last_name': 'Piveteau', 'gender': 2, 'hire_date': '1989-08-24'} <class 'rest_framework.utils.serializer_helpers.ReturnDict'>
OrderedDict([('emp_no', 10010), ('birth_date', datetime.date(1963, 6, 1)), ('first_name', 'Duangkaew'), ('last_name', 'Piveteau'), ('gender', 2), ('hire_date', datetime.date(1989, 8, 24))])
gender给个3试一试?报错了,因为Employee.Gender.choices是定义的约束,校验失败,如下图
校验
对浏览器端提交的数据一定要校验,所以,校验是入库前必须要做的
字段选项参数校验
- 字符串长度
- 长度测试min_length和max_length
first_name = serializers.CharField(label='长度限制和必须',max_length=14,min_length=3)
注意:异常返回的[]列表,说明一个字段可以有多个校验器,这和前端开发校验一样
- 字段值是否必须
- require=True,默认值,序列化、反序列化都必须提供,否则报错,缺失时可以使用default提供缺省值
- require=False
- 反序列化时,字段值提供就要校验,不提供就不校验,校验成功就放到validated_data,校验该字段失败抛异常
- 序列化时,有则输出,如果有default,缺失时使用缺省值补充
- default=’abcd’,序列化、反序列化,缺失该字段就补充缺省值
last_name = serializers.CharField(max_length=16,default='abb')
- 只读
- read_only不校验该字段,只会出现在序列化中,有也行,没有也行,有就输出给人看
- read_only和require不可以同时为True,这是矛盾的
- read_only=True表示序列化时可以序列化t2这个属性的数据,反序列化不使用它。反序列化时提供了该值也不校验也不输出,serializer.data输出结果中没有t2
t2 = serializers.CharField(read_only=True)
- 只写
- write_only=True,表示反序列化用,要校验该字段。不会被序列化,不返回给浏览器
- .data属性中没有,要在.validated_data属性查看,因为入库用的时validated_data
t3 = serializers.CharField(write_only=True)
对序列化器略作修改做测试,这个测试主要测试的时反序列化的校验
from rest_framework import serializers
from .models import Employee
class EmpSerializer(serializers.Serializer):
emp_no = serializers.IntegerField()
birth_date = serializers.DateField()
first_name = serializers.CharField(max_length=14)
last_name = serializers.CharField(max_length=16)
gender = serializers.ChoiceField(choices=Employee.Gender.choices)
hire_date = serializers.DateField()
t1 = serializers.CharField(label="长度限制和必须", min_length=4,
max_length=8)
t2 = serializers.CharField(read_only=True)
t3 = serializers.CharField(write_only=True)
import os
import django
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'salary.settings')
django.setup(set_prefix=False)
# 所有测试代码,都要在上面4行之下
from employee.models import Employee
from employee.serializers import EmpSerializer
emgr = Employee.objects
# 单个对象
emp = emgr.get(pk=10010)
print(emp.__dict__)
# 为序列化凑几个字段
emp.t1 = 't1' # 长度为2
emp.t2 = 't2'
emp.t3 = 't3'
# 给实例准备序列化为Json字符串
serializer = EmpSerializer(instance=emp)
data = serializer.data # 序列化
print(data) # 字典
序列化结果如下:
- 序列化时,不校验t1
- t2是read_only,所以有它
- t3是write_only,不参与序列化,所以没有它
{'emp_no': 10010, 'birth_date': '1963-06-01', 'first_name': 'Duangkaew',
'last_name': 'Piveteau', 'gender': 2, 'hire_date': '1989-08-24', 't1': 't1',
't2': 't2'}
import os
import django
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'salary.settings')
django.setup(set_prefix=False)
# 所有测试代码,都要在上面4行之下
from employee.models import Employee
from employee.serializers import EmpSerializer
emgr = Employee.objects
# POST方法提交上来的数据被request封装为字典
data = {
'emp_no': 10010, 'birth_date': '1963-06-01',
'first_name': 'Duangkaew', 'last_name': 'Piveteau',
'gender': "2", 'hire_date': '1989-08-24',
't1':'abcd',
't2':'t2++', # t2给不给无所谓
't3':'t3=='
} # 注意,这是字典数据是data,不是Employee的实例
serializer = EmpSerializer(data=data)
# print(serializer.data) # 没有调用.is_valid()直接报错,第一步需要验证
validated = serializer.is_valid(raise_exception=True)
# is_valid方法调用内部成功会把数据放在_validated_data属性
# raise_exception验证失败是否抛异常
print(validated)
print(serializer.data)
print(serializer.validated_data)
反序列化结果如下:
- 校验t1
- t2是read_only,所以没有它
- t3是write_only,反序列化必须有它,还要校验它
{'emp_no': 10010, 'birth_date': datetime.date(1963, 6, 1), 'first_name':
'Duangkaew', 'last_name': 'Piveteau', 'gender': 2, 'hire_date':
datetime.date(1989, 8, 24), 't1': 'abcd', 't3': 't3=='}
字段级校验器
参考: https://www.django-rest-framework.org/api-guide/serializers/#field-level-validation
看官网,非常清楚
from rest_framework import serializers
from .models import Employees
class EmployeeSerializer(serializers.Serializer):
emp_no = serializers.IntegerField()
birth_date = serializers.DateField()
first_name = serializers.CharField() ##使用单独字段的校验器
last_name = serializers.CharField(max_length=16,default='abb')
gender = serializers.ChoiceField(Employees.Gender.choices)
hire_date = serializers.DateField()
def validate_first_name(self, value):
"""
Check that the blog post is about Django.
"""
print(value,type(value),len(value))
if len(value) < 3 or len(value) >14:
raise serializers.ValidationError("长度必须大于3,小于14")
return value
注意:value可以在这里被替换成其他值
还用一种写法validators=[validate_first_name]
def validate_first_name( value):
print(value, type(value), len(value))
if len(value) < 3 or len(value) > 14:
return 'abcd'
# raise serializers.ValidationError("长度必须大于3,小于14")
return value
class EmployeeSerializer(serializers.Serializer):
emp_no = serializers.IntegerField()
birth_date = serializers.DateField()
first_name = serializers.CharField(validators=[validate_first_name])
last_name = serializers.CharField(max_length=16,default='abb')
gender = serializers.ChoiceField(Employees.Gender.choices)
hire_date = serializers.DateField()
除非有复用的必要,不要把校验器定义在外部
import os
import django
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'salary.settings')
django.setup(set_prefix=False)
# 所有测试代码,都要在上面4行之下
from employee.models import Employee
from employee.serializers import EmpSerializer
emgr = Employee.objects
# POST方法提交上来的数据被request封装为字典
data = {
'emp_no': 10010, 'birth_date': '1963-06-01',
'first_name': 'Duangkaew', 'last_name': 'Piveteau',
'gender': "2", 'hire_date': '1989-08-24',
} # 注意,这是字典数据是data,不是Employee的实例
serializer = EmpSerializer(data=data)
# print(serializer.data) # 没有调用.is_valid()直接报错,第一步需要验证
validated = serializer.is_valid(raise_exception=True)
# is_valid方法调用内部成功会把数据放在_validated_data属性
# raise_exception验证失败是否抛异常
print(validated)
print(serializer.data)
print(serializer.validated_data)
对象级校验器
参考https://www.django-rest-framework.org/api-guide/serializers/#object-level-validation
对象级校验器就是对实例所有字段数据的校验
class EmployeeSerializer(serializers.Serializer):
emp_no = serializers.IntegerField()
birth_date = serializers.DateField()
first_name = serializers.CharField()
last_name = serializers.CharField(max_length=16,default='abb')
gender = serializers.ChoiceField(Employees.Gender.choices)
hire_date = serializers.DateField()
def validate(self, data):
print('_______',data)
if data['birth_date'] > data['hire_date']:
raise serializers.ValidationError("finish must occur after start")
return data
入库
参考: https://www.django-rest-framework.org/api-guide/serializers/#saving-instances
校验后的数据是安全的,可以写入数据库。
原理
BaseSerializer中定义了save()方法
- save()之前一定要调用.is_valid()
- BaseSerializer(instance=None,data=empty),无实例就是新增,调用create;有实例就是更新,调用update。最终返回实例
- 使用的是validated_data
- 在BaseSerializer中,create、update是未实现的抽象方法,Serializer也没有实现这2个方法。所以就需要用户自己实现。
from rest_framework import serializers
from .models import Employees
class EmployeeSerializer(serializers.Serializer):
emp_no = serializers.IntegerField()
birth_date = serializers.DateField()
first_name = serializers.CharField()
last_name = serializers.CharField(max_length=16,default='abb')
gender = serializers.ChoiceField(Employees.Gender.choices)
hire_date = serializers.DateField()
# 序列化器的基类只负责调用create或update,但未实现
def create(self, validated_data):
# validated_data校验过的数据
# 管理器实现了create方法完成数据新增
return Employees.objects.create(**validated_data)
def update(self, instance, validated_data):
instance.birth_date = validated_data.get('birth_date', instance.birth_date)
instance.first_name = validated_data.get('first_name', instance.first_name)
instance.last_name = validated_data.get('last_name', instance.last_name)
instance.gender = validated_data.get('gender', instance.gender)
instance.hire_date = validated_data.get('hire_date', instance.hire_date)
return instance
注意:序列化器只负责实现、调用save、create、update等方法,但是数据库的CRUD操作依然是Model类的objects完成的
增
import os
import django
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'salary.settings')
django.setup(set_prefix=False)
# 所有测试代码,都要在上面4行之下
from employee.models import Employee
from employee.serializers import EmpSerializer
emgr = Employee.objects
# POST方法提交上来的数据被request封装为字典
data = {
'emp_no': 10021, 'birth_date': '1963-06-01',
'first_name': 'san', 'last_name': 'zhang',
'gender': "1", 'hire_date': '1989-08-24',
} # 注意,这是字典数据是data,不是Employee的实例
# 只有data没有实例,是新增
serializer = EmpSerializer(data=data)
validated = serializer.is_valid(raise_exception=True)
print(serializer.save())
print('=' * 30)
print(serializer.data)
改
先查再改:先查返回结果填充实例,然后根据这个实例修改数据
import os
import django
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'salary.settings')
django.setup(set_prefix=False)
# 所有测试代码,都要在上面4行之下
from employee.models import Employee
from employee.serializers import EmpSerializer
emgr = Employee.objects
emp = emgr.get(pk=10021)
print(emp)
# POST方法提交上来的数据被request封装为字典
data = {
'emp_no': 10021, 'birth_date': '1963-06-01',
'first_name': 'si', 'last_name': 'li',
'gender': 1, 'hire_date': '1989-08-24',
} # 注意,这是字典数据是data,不是Employee的实例
# 有实例,有data,是更新
serializer = EmpSerializer(emp, data=data)
validated = serializer.is_valid(raise_exception=True)
print(serializer.save())
print('=' * 30)
print(serializer.data)
修改或增加完后,数据库中对应的记录就会被增加或更改了
总结
- 序列化
- 查库:查询数据库得到单个实例或多个实例
- 构造序列化器实例:构造序列化器实例
- 输出:使用data属性获取字典
- 反序列化
- 反序列化:将JSON数据反序列化为字典
- 构造序列化器实例:使用字典构造序列化器实例
- 校验:调用序列化器的is_valid方法校验各个字段的值
- 入库:调用序列化器的save方法,背后调用了序列化器的create、update方法,本质上用的是Model类写库