一、序列化和反序列化
api接口开发,最核心最常见的一个过程就是序列化,所谓序列化就是把数据转换格式,序列化可以分两个阶段:
序列化: 把我们语言识别的数据转换成指定的格式提供给别人。
如python中的字典、列表、对象...只能在python中识别,在其他语言或前端中,不能识别
我们可以通过json、xml、prop、massagepack等转换成别的都能识别的格式,这一过程叫做序列化
反序列化:把别人提供的数据转换/还原成我们需要的格式
前端为了后端能够识别自己语言格式,也会把要传输的数据序列化
后端接收到序列化的数据,并不能直接使用,必须把这些数据转换成自己语言的格式,这一过程要做反序列化
现在最流行的序列化和反序列化工具是json
二、序列化器
1、序列化器的作用
序列化器,在Django框架终究是一个类,她的作用就是序列化和反序列化
序列化:序列化会把(数据库中的)模型对象转换成字典,经过response以后变成字符串,传给客户端(前端)
[{"name":"西游记","price":"100.00","author":"吴承恩"}]
反序列化:把客户端(前端)送过来的数据,经过request后,变成字典,序列化器可以把字典再转换成模型,最后再存到数据库中
序列化器在反序列化过程中,会自动完成数据校验(数据是否合法,长度是否足够)。
2、序列化器的使用方式(口头描述)
第一步:写一个类:必须继承drf中的Serializer及其子类
第二步:在类中写要序列化的字段(要序列化哪些,就写哪些,不序列化的可以不写)
第三步:在视图类中使用序列化类
第四步:得到序列化类对象(对象.data),通过Response返回给前端
三、序列化类—Serializer的使用
1、序列化
(1)查询所有
1. 表模型类
class Book(models.Model):
name = models.CharField(max_length=32)
price = models.DecimalField(decimal_places=2, max_digits=5)
author = models.CharField(max_length=32)
2.序列化器
from rest_framework import serializers
from .models import Book
class BookSerializer(serializers.Serializer):
# max_length=32,min_length=3 反序列化保存校验数据的时候用
name = serializers.CharField(max_length=8, min_length=3)
price=serializers.CharField() # models中使用了DecimalField,这个位置使用了CharField会把小数类型转成字符串
author = serializers.CharField()
3.视图类
from rest_framework.response import Response
from app01.models import Book
from rest_framework.views import APIView
from app01.serializer import BookSerializer
class BookView(APIView):
def get(self, request):
# 查出来的数据做序列化
book_list = Book.objects.all()
# instance:要序列化的对象 qs,单个对象
# many:如果是qs对象,many=True,如果是单个对象many=False
ser = BookSerializer(instance=book_list, many=True) # 传入初始化的参数instance=None, data=empty
# ser.data使用模型类对象序列化后的字典
return Response(ser.data) # 字典,列表,字符串都行
4.路由
from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^books/', views.BookView.as_view()),
]
(2)字段类型
models中有自己的字段类型
name = models.CharField()
price = models.DecimalField()
序列化器中也有自己的字段类型
name = serializers.CharField()
price=serializers.CharField()
常用的序列化器字段类型:
CharField
IntegerField
FloatField
DecimalField
DateTimeField
DateField
字段大全
字段 | 字段构造方式 |
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) |
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=) |
(3)字段参数
models中有自己的字段参数
name = models.CharField(max_length=32)
price = models.DecimalField(max_digits=8, decimal_places=2)
序列化器中也有自己的字段参数
name = serializers.CharField(max_length=8, min_length=2)
注意
'''
read_only:表明该字段仅用于序列化输出,默认False
-如果read_only=True,这个字段只用来做序列化---》把对象---》json给前端
write_only:表明该字段仅用于反序列化输入,默认False
-如果read_only=write_only,这个字段只用来做反序列化---》前端json---》存到数据库'''
选项参数
参数名称 | 作用 |
max_length | 最大长度(CharField) |
min_lenght | 最小长度(CharField) |
allow_blank | 是否允许为空(CharField) |
trim_whitespace | 是否截断空白字符(CharField) |
max_value | 最小值 (IntegerField) |
min_value | 最大值(IntegerField) |
通用参数:
参数名称 | 说明 |
read_only | 表明该字段仅用于序列化输出,默认False |
write_only | 表明该字段仅用于反序列化输入,默认False |
required | 表明该字段在反序列化时必须输入,默认True |
default | 反序列化时使用的默认值 |
allow_null | 表明该字段是否允许传入None,默认False |
validators | 该字段使用的验证器(不太用) |
error_messages | 包含错误编号与错误信息的字典 |
label | 用于HTML展示API页面时,显示的字段名称 |
help_text | 用于HTML展示API页面时,显示的字段帮助提示信息 |
(4)定制序列化字段
我们可以通过序列化器自带的方法,自定义我们想要的序列化字段,key(字段名),value(方法返回)都可以定制
比如,作者表和作者详情表(一对一外键关系)
我们可以序列化作者表(作者表里有姓名),并在作者表里自定义作者信息字段
这样我们就可以通过一张表,返回我们想要的(作者,作者信息)序列化数据
方式一:序列化类中定制
from rest_framework import serializers
from app01.models import Book
class BookSerializer(serializers.Serializer):
name = serializers.CharField(max_length=8, min_length=2)
price = serializers.CharField()
author = serializers.CharField()
# 定制序列化字段re_name
re_name = serializers.SerializerMethodField() # 必须配合一个方法,方法名get_字段名
def get_re_name(self, obj): # 方法返回什么,字段就是什么,obj是当前序列化到的单个对象
return str(obj.name) + '重置版' # python是动态强类型语言--》强类型是:不同类型之间不允许直接运算
# 定制序列化字段price_info
price_info = serializers.SerializerMethodField()
def get_price_info(self, obj):
return "价格是:" + str(obj.price)
方式二:models中写字段,serializer中序列化
models
在表模型中定制想要的字段方法(函数),并伪装成属性,所以不需要做表迁移
class Book(models.Model):
name = models.CharField(max_length=32)
price = models.DecimalField(max_digits=8, decimal_places=2)
author = models.CharField(max_length=32)
@property
def re_name(self):
return str(self.name) + '重置版'
@property
def price_info(self):
return '价格是' + str(self.price)
serializer
class BookSerializer(serializers.Serializer):
name = serializers.CharField(max_length=8, min_length=2)
price = serializers.CharField()
author = serializers.CharField()
# 在models中写方法,这里字段名就是models的方法名,price_info方法返回什么,这个字段就是什么
re_name = serializers.CharField()
price_info = serializers.CharField()
注意:如果是列表,用ListField()方法;如果是字典,用DecimalField()方法
authors=serializers.ListField()
authors_info = serializers.DecimalField()
2、反序列化
1.反序列化—新增creare
反序列化顺序
# 第一步:把前端传入的数据,放到Serializer对象中:ser=BookSerializer(data=request.data)
# 第二步:校验数据:ser.is_valid():
# 第三步:保存,ser.save()---》必须重写create,在序列化类中
def create(self, validated_data):
book = Book.objects.create(**validated_data)
return book
代码演示
视图类
class BookView(APIView):
# 反序列化
def post(self, request):
# 接收前端返回的数据,使用data参数
ser = BookSerializer(data=request.data)
# 校验参数
if ser.is_valid():
# 如果是true表示数据校验通过,通过,就保存
# 如果instance为None,调用save本质会调用create--》父类create直接抛异常,所以我们要重写create
ser.save()
return Response(ser.data)
return Response({'code': 101, 'msg': '数据校验失败', 'err': ser.errors})
序列化器
class BookSerializer(serializers.Serializer):
name = serializers.CharField(max_length=8, min_length=2)
price = serializers.CharField()
author = serializers.CharField()
# 定制字段:在models中写方法,这里字段名就是models的方法名,price_info方法返回什么,这个字段就是什么
re_name = serializers.CharField(read_only=True) # 只序列化
price_info = serializers.CharField(read_only=True) # 只序列化
def create(self, validated_data):
# validated_data校验过后的数据
# 手动存到book表中
book = Book.objects.create(**validated_data)
# book=Book.objects.create(name=validated_data.get('name'))
return book # 不要忘记返回book对象
2.反序列化—修改;系列化—查询单条、删除单条
视图类
class BookDetailView(APIView):
def get(self, request, pk):
book = Book.objects.filter(pk=pk).first() # 查询单条记录
ser = BookSerializer(instance=book) # 如果是单条记录,many不传,就是false
return Response(ser.data)
def delete(self, request, pk):
res = Book.objects.filter(pk=pk).delete() # 删除单条记录
print(res)
if res[0] > 0:
return Response({'code': 100, 'msg': '删除成功'})
else:
return Response({'code': 103, 'msg': '数据不存在'})
def put(self, request, pk):
# 修改:用什么数据,修改哪个对象?
book = Book.objects.filter(pk=pk).first()
# 既有instance,又有data,表示修改
ser = BookSerializer(instance=book, data=request.data)
if ser.is_valid():
# 重写update方法
ser.save() # 调用save---》内部根据instance判断是触发create还是update
return Response(ser.data)
return Response({'code': 102, 'msg': '修改出错', 'err': ser.errors})
序列化类
class BookSerializer(serializers.Serializer):
id = serializers.CharField(read_only=True)
# max_length=32,min_length=3 反序列化保存校验数据的时候用
name = serializers.CharField(max_length=8, min_length=3)
price = serializers.CharField(required=False) # models中使用了DecimalField,这个位置使用了CharField会把小数类型转成字符串
author = serializers.CharField(required=False)
def create(self, validated_data):
# validated_data校验过后的数据
# 手动存到book表中
book = Book.objects.create(**validated_data)
# book=Book.objects.create(name=validated_data.get('name'))
return book # 不要忘记返回book对象
def update(self, instance, validated_data):
# validated_data校验过后的数据,instance 是要修改的对象
instance.name = validated_data.get('name')
instance.price = validated_data.get('price')
instance.author = validated_data.get('author')
instance.save() # 模型对象自带的save,保存到数据库中
return instance # 不要忘记返回instance对象
# 局部钩子
# 字段有自己的校验:max_length .... ,再校验,就可以写局部钩子
def validate_name(self, attr):
# attr就是前端传入的数据
# 名字不能以sb开头
if attr.startswith('sb'):
raise ValidationError("名字不能以sb开头")
else:
return attr # 没有问题,正常返回
路由
urlpatterns = [
path('admin/', admin.site.urls),
path('books/', views.BookView.as_view()),
path('books/<int:pk>', views.BookDetailView.as_view()), # 用于查询一个,利用转换器<int:pk>
]
3.反序列化—局部和全局钩子
虽然序列化器会根据字段参数对字段自动校验,但是某些情况,需要我们主动添加一些校验,这里就要用到局部钩子和全局钩子的概念
from rest_framework.exceptions import ValidationError
# 局部钩子
# 字段有自己的校验:max_length .... ,再校验,就可以写局部钩子
def validate_name(self, attr):
# attr就是前端传入的数据
# 名字不能以sb开头
if attr.startswith('sb'):
raise ValidationError("名字不能以sb开头")
else:
return attr # 没有问题,正常返回
# 先走字段自己规则,再走局部钩子,再走全局钩子
# 全局钩子
def validate(self, attrs):
# attrs校验过后的数据
if attrs.get('name') == attrs.get('author'):
raise ValidationError('作者名不能等于书名')
else:
return attrs
四、模型类序列化器—ModelSerializer
模型类序列化器是与表模型是绑定关系,他通过对序列化器的封装,可以让我们省去重写updata、create
注意,如果定制序列化字段是跨表新增和修改,依然需要重写updata、create
1、绑定
# 以后使用ModelSerializer跟表模型做绑定,以后这个用的多,不需要重写update和create方法了
class BookSerializer2(serializers.ModelSerializer):
class Meta:
model = Book # 跟那个表有关系,绑定
# fields='__all__' # 这是注册的第一种方式,表示序列化所有字段
fields = ['id', 'name', 'price', 'author', 'price_info'] # 这是注册的第二种方式,序列化自己想要的字段
# id是从表模型中映射过来的,auto,它会不要求你传
# price_info:它不是数据库中字段,使我们扩写的字段
# 注意:即便扩写的字段,也要在fields中注册# 重写字段
# 局部和全局钩子跟之前一样
2、额外添加参数
# 以后使用ModelSerializer跟表模型做绑定,以后这个用的多,不需要重写update和create方法了
class BookSerializer2(serializers.ModelSerializer):
class Meta:
model = Book # 跟那个表有关系,绑定
# fields='__all__' # 这是注册的第一种方式,表示序列化所有字段
fields = ['id', 'name', 'price', 'author', 'price_info'] # 这是注册的第二种方式,序列化自己想要的字段
# id是从表模型中映射过来的,auto,它会不要求你传
# price_info:它不是数据库中字段,使我们扩写的字段
# 注意:即便扩写的字段,也要在fields中注册
# 原来的字段参数,通过extra_kwargs传进去
extra_kwargs = {
'name': {'write_only': True, 'max_length': 8, 'min_length': 3}
}
# 重写字段
# 局部和全局钩子跟之前一样,但是要注意写在Meta外
五、序列化多表操作
1、代码
多表模型
from django.db import models
class Book(models.Model):
name = models.CharField(max_length=32, )
price = models.DecimalField(max_digits=8, decimal_places=2)
publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE)
authors = models.ManyToManyField(to='Author')
def __str__(self):
return self.name
@property
def publish_detail(self):
return {'name': self.publish.name, 'addr': self.publish.city}
@property
def author_list(self):
l = []
for author in self.authors.all():
l.append({'name': author.name, 'addr': author.author_detail.addr})
return l
class Author(models.Model):
name = models.CharField(max_length=32)
age = models.IntegerField()
author_detail = models.OneToOneField(to='AuthorDetail', on_delete=models.CASCADE)
class AuthorDetail(models.Model):
phone = models.BigIntegerField()
birthday = models.DateTimeField()
addr = models.CharField(max_length=64)
class Publish(models.Model):
name = models.CharField(max_length=32, )
city = models.CharField(max_length=32, )
email = models.EmailField(max_length=32, )
序列化器
from app01 import models
from rest_framework.serializers import ModelSerializer
class BookSerializer(ModelSerializer):
class Meta:
model = models.Book
# fields = '__all__'
# depth = 1
fields = ['id', 'name', 'price', 'publish', 'authors', 'publish_detail', 'author_list']
extra_kwargs = {
'publish': {'write_only': True},
'authors': {'write_only': True}
}
class AuthorSerializer(ModelSerializer):
class Meta:
model = models.Author
fields = '__all__'
class AuthorDetailSerializer(ModelSerializer):
class Meta:
models = models.AuthorDetail
fields = '__all__'
class PublishSerializer(ModelSerializer):
class Meta():
model = models.Publish
fields = '__all__'
视图接口
from rest_framework.response import Response
from rest_framework.views import APIView
from app01 import models
from app01 import serializer
class BookView(APIView):
def get(self, request):
book_list = models.Book.objects.all()
ser = serializer.BookSerializer(instance=book_list, many=True)
return Response(ser.data)
def post(self, request):
ser = serializer.BookSerializer(data=request.data)
if ser.is_valid():
ser.save()
return Response({"code": 100, "msg": "新增成功", "data": ser.data})
return Response({"code": 101, "msg": "新增失败", "err": ser.errors})
class BookDetailView(APIView):
def get(self, request, pk):
book = models.Book.objects.all().filter(pk=pk).first()
ser = serializer.BookSerializer(instance=book)
return Response(ser.data)
def put(self, request, pk):
book = models.Book.objects.all().filter(pk=pk).first()
ser = serializer.BookSerializer(instance=book, data=request.data)
if ser.is_valid():
ser.save()
return Response({"code": 100, "msg": "修改成功", "data": request.data})
return Response({"code": 102, "msg": "修改失败", "err": ser.errors})
def delete(self, request, pk):
models.Book.objects.filter(pk=pk).delete()
return Response({"code": 100, "msg": "删除成功"})
路由
from django.contrib import admin
from django.urls import path
from app01 import views
urlpatterns = [
path('admin/', admin.site.urls),
path('books/', views.BookView.as_view()),
path('books/<int:pk>', views.BookDetailView.as_view()),
]
2、验证
查看所有
添加一个
查看一个
修改一个
删除一个
六、请求和响应
这里的请求和响应是经过drf封装之后的请求和响应,增加了一些内容
请求
request.data 请求的数据
request.query_params 请求的方式(get、post)
可以通过局部配置和全局配置,设置能够解析的数据格式
# Request 类的对象---》新的request对象
from rest_framework.request import Request
# 记住的
__getattr__
request.data
request.query_parmas--->self._request.GET-->restful规范里,请求地址中带过滤(查询)条件---》get请求地址中提交的数据在GET中,---》query_parmas:查询参数
# 了解--->默认情况下,可以解析 urlencoded,formdata,json
-如果我们写了一个接口,想只能处理json格式,或者只能处理formdata
# 局部配置
from rest_framework.parsers import JSONParser,FormParser,MultiPartParser
class PublishView(APIView):
# 局部使用,只针对当前视图类有效,只想处理json格式
parser_classes = [JSONParser]
# 全局配置---》配置文件--->所有接口都只能解析json格式
REST_FRAMEWORK = {
'DEFAULT_PARSER_CLASSES': [
'rest_framework.parsers.JSONParser',
],
}
# 全局配置解析json,局部某个视图函数想能解析formdata格式
视图类中配置一下即可---》局部配置
-如果局部配置如下,会怎么样
parser_classes = [] # 所有格式都补不能解析了
# 使用顺序:我们没有配置,也有默认配置:3个都能解析
drf有默认配置(最后)----》项目配置文件的配置(其次)----》视图类中配的(优先用)
drf的默认配置:from rest_framework import settings
# 总结:一般情况下,都使用默认即可,不用配置
View Code
响应
response.data 写在响应体中,返回给前端的数据
response.status 响应状态码
response.header 响应头
Respone:from rest_framework.response import Response
# 属性
data=None, # 字符串,字典,列表--》给http响应body体中内容-->response对象中取出处理
status=None, # 响应状态码:1xx,2xx,3xx,默认是200
from rest_framework.status import HTTP_201_CREATED
Response(ser.data,status=HTTP_201_CREATED)
headers=None, # 响应头 字典
---了解---
template_name=None, # 模板名字(不用),用浏览器访问时,可以改
exception=False, # 异常处理
content_type=None # 响应编码格式
# 响应格式---》跟解析格式
# 局部设置
from rest_framework.renderers import JSONRenderer,BrowsableAPIRenderer
class BookDetailView(APIView):
renderer_classes = [JSONRenderer,]
# 全局设置
REST_FRAMEWORK = {
'DEFAULT_RENDERER_CLASSES': ( # 默认响应渲染类
'rest_framework.renderers.JSONRenderer', # json渲染器
'rest_framework.renderers.BrowsableAPIRenderer', # 浏览API渲染器
)
}
View Code