序列化
一. 序列化初步认识
# 序列化
后端的数据----转化---->提供给前端
# 反序列化
前端的数据----转化---->后端接收
二. 基于《serializer》基类
serializer是基类,序列化类都要继承它, 但需要写字段,在 “创建” 和 “修改” 时, 需要重写create和updata方法。
1. 《 序列化 》
from rest_framework import serializers
from app01 import models
class PublishSerializer(serializers.Serializer):
# 因为继承 Serializer,需要写序列化的字段
id = serializers.IntegerField()
name = serializers.CharField()
addr = serializers.CharField()
from django.urls import path
from app01 import views
urlpatterns = [
path('books/', views.PublishView.as_view()),
path('books/<int:pk>', views.PublishDetailView.as_view()),
]
from rest_framework.views import APIView
from rest_framework.response import Response
from app01 import models
from .serializer import PublishSerializer
class PublishView(APIView):
# 获取单条数据
def get(self, request):
publish_list = Publish.objects.all()
ser = PublishSerializer(instance=publish_list, many=True) # 如果序列化多条,要many=True
return Response({'code': 100, 'msg': '查询所有成功', 'results': ser.data})
class PublishDetailView(APIView):
# 获取多条数据
def get(self, request, pk):
publish = Publish.objects.filter(pk=pk).first()
ser = PublishSerializer(instance=publish) # 单个不写many=True
return Response(
{'code': 100, 'msg': '查询单条成功', 'results': ser.data})
2. 《 反序列化 + 校验 》
一共有三层校验
class PublishSerializer(serializers.Serializer):
# 第一层校验
id = serializers.IntegerField()
# error_messages 不想写这个就对 django 做中文配置, 记得注册 rest_framework。
name = serializers.CharField((max_length=8, min_length=3, error_messages={
"max_length": '太长了,不能超过8字符',
"min_length": '太短了,不能少于3字符'
})
addr = serializers.CharField()
# 第二层校验《局部钩子》 validate_需要校验的字段名(self, 需要校验的字段名):
def validate_name(self, name):
# 待校验的前端传入的name的数据
if name.startswith("sb"):
# 不行,抛异常
raise ValidationError('不能以sb开头')
return name
# 第三层校验《全局钩子》 attrs: 前端传来的全部字段(一中的全部字段), 第一层没写的字段, attrs中则不会有。
def validate(self, attrs):
print(attrs)
# 多个字段同时校验
if attrs.get('name')[:3] == attrs.get('addr')[:3]:
raise ValidationError('出版社名和地址不能一样')
return attrs
3. 《 反序列化 + 校验 + 保存 》
需要重写create和updata方法。
is_valid() 后下面不要打印 res.data
if res.is_valid():
print(res.data) # 这里一定不要打印, 会报错。
# 报错信息: AssertionError: You cannot call `.save()` after accessing `serializer.data`.If you need to access data before committing to the database then inspect 'serializer.validated_data' instead.
from rest_framework import serializers
from .models import Book
class BookSerializer(serializers.Serializer):
# read_only=True 意思是只读,只用来做序列化,不用来做反序列化
# 如果不写read_only=True,这个字段必传,如果不传,数据校验不过
# 如果写了read_only=True,这个字段不传
id=serializers.IntegerField(read_only=True) # model中Auto,本质就是数字,IntegerField
name=serializers.CharField(max_length=8,min_length=3,error_messages={'max_length':'太长了'})
# price=serializers.IntegerField()
price=serializers.CharField(max_length=1000) # 写成CharField也能映射成功
def create(self, validated_data): # 代码一点没少写,甚至多了,好处解耦了,view代码少了
# validated_data就是校验过后的数据
# 高级
book=Book.objects.create(**validated_data)
# 菜鸡
# name=validated_data.get('name')
# price=validated_data.get('price')
# book = Book.objects.create(name=name,price=price)
return book # 一定要return新增的对象
def update(self, instance, validated_data):
# instance 是要修改的对象
# validated_data是校验过后数据
instance.name=validated_data.get('name')
instance.price=validated_data.get('price')
instance.save() # 一定不要忘了保存,才存到数据库
return instance # 一定要return新增的对象
# 利用反射方式:《装13》
def update(self, instance, validated_data):
for key in validated_data:
setattr(instance, key, validated_data.get(key))
instance.save()
return instance
from rest_framework.views import APIView
from rest_framework.response import Response
from .serializer import BookSerializer
# 图书新增:psot 图书查询所有:get
class BookView(APIView):
def post(self,request):
# 反序列化--->传得是data=前端传入的数据request.data
ser=BookSerializer(data=request.data)
# 数据校验
if ser.is_valid(): # forms组件就这么做的
# 保存-->会报错---->需要在序列化类中重写create方法
ser.save()
return Response({'msg':'新增成功','code':100, 'data': ser.data})
else:
print(ser.errors)
return Response({'msg': '数据没有校验通过', 'code': 101, 'err': ser.errors})
# 图书查询一个:get ,图书修改一个:put ,图书删除一个:delete
class BookDetailView(APIView):
def put(self,request,pk):
# 修改,就有book对象,拿到要修改的对象
book=Book.objects.filter(pk=pk).first()
# 使用data的数据,修改book这个对象
ser = BookSerializer(instance=book,data=request.data)
if ser.is_valid():
# 会报错,需要重写序列化类的updata方法
ser.save()
return Response({'code':100,'msg':"修改成功", 'data': ser.data})
else:
return Response({'code': 101, 'msg': "修改失败", 'err': ser.errors})
三. 自定义还回字段 《基于 serializer 基类》
注意:
1. 当反序列化处理 外键 时, 序列化类 字段需要写成 外键名_id (eg: aothor_id), 保存值时同样用《外键名_id》。 2. 当序列化与反序列化字段不一致时, 需要用 read_only 和 write_only做处理。1. 方式一
在模型表(models.py)中实现
# models.py
class Book(models.Model):
name = models.CharField(max_length=60)
publish = models.CharField(max_length=60)
author = models.ForeignKey(to='Author', on_delete=models.CASCADE)
# 1. 重写一个方法,返回自己想要的格式。
# 这就是自定义字段
def author_obj(self):
return {"id": self.author.pk, "name": self.author.name}
# serializer.py
class BookSerializers(serializers.Serializer):
...
# 2. 接收 <1> 中的方法,字段名要和方法名一样。《可以不一样, 但需要用 source 做映射》
# <1> 中还回什么类型,就用什么类型接收,比如:DictField
author_obj = serializers.DictField(required=False)
2. 方式二
在序列化器(serializer.py)中实现
class BookSerializers(serializers.Serializer):
# 在这里接收 <1> 中的方法,需要用到 SerializerMethodField
author_detail = serializers.SerializerMethodField(read_only=True)
# 1. 同样写一个方法, 命名为《get__字段名》
def get_author_detail(self, obj):
return {"id": obj.author.pk, "name": obj.author.name, "age": obj.author.age + 50}
3. 正反向查询时《补充》
通过 _set 反向查询, 本质就是 ORM 相关操作
eg: 通过反向查询, 查找本作者所有的 book。
"""---------------------------------------models.py---------------------------------------"""
class Book(models.Model):
name = models.CharField(max_length=60)
publish = models.CharField(max_length=60)
author = models.ForeignKey(to='Author', on_delete=models.CASCADE)
class Author(models.Model):
name = models.CharField(max_length=60)
age = models.IntegerField()
# 1. 方式一
def book_list(self):
l = []
print(self)
for i in self.book_set.all(): # book_set
print(i)
l.append({'name': i.name, 'publish': i.publish})
return l
"""---------------------------------------serializer.py---------------------------------------"""
class AuthorSerializers(serializers.ModelSerializer):
class Meta:
model = Author
# 《 方式一, 方式二都需要在这里映射 》
fields = ['name', 'age', 'book_list']
book_list = serializers.SerializerMethodField()
# 2. 方式一
def get_book_list(self, obj):
l = []
print('obj')
for i in obj.book_set.all():
# print(i)
l.append({'name': i.name, 'publish': i.publish})
return l
四. 序列化字段及参数
字段类:跟 models 对应,但是比它多
# BooleanField
# NullBooleanField
# CharField
# EmailField
# RegexField
# SlugField
# URLField
# UUIDField
# IPAddressField
# IntegerField
# FloatField
# DecimalField
# DateTimeField
# DateField
# TimeField
# ChoiceField
# FileField
# ImageField
以上都是models有的----下面是serializer独有的 # (在自定义还回字段可以用到)
# ListField
# DictField
# PrimaryKeyRelatedField
PrimaryKeyRelatedField
重写字段时,ListField 对应的是一个个 “id”, PrimaryKeyRelatedField 对应的是一个个 “对象”
# ListField
[1, 2, 3, 4]
# PrimaryKeyRelatedField
[obj1, obj2, obj3, obj4]
"""---------------使用-----------------------"""
# 1. 前端传入
courses = [1, 2, 3, 4]
# 2. 后端接收
# 重写字段 queryset 要与指定的表做对应,many=True 多条时需要指定。
courses = serializers.PrimaryKeyRelatedField(queryset=Course.objects.all(),many=True)
# 映射
class Meta:
model = Order
fields = [
'courses', # 不是表中字段,需要重写
]
字段校验参数:
# CharField
max_length # 最大长度
min_lenght # 最小长度
allow_blank # 是否允许为空
trim_whitespace # 是否截断空白字符
# IntegerField
max_value # 最小值
min_value # 最大值
# 通用参数:
read_only # 用于序列化输出(只读),默认False
write_only # 用于反序列化输入(只写),默认False
required # 反序列化时必须输入 (必填),默认True
default # 反序列化时使用的默认值
allow_null # 表明该字段是否允许传入None,默认False
error_messages # 包含错误编号与错误信息的字典
五. ModelSerializer
1. 继承 ModelSerializer,可以不写字段(因为字段跟表有对应关系),也可以不写update和create
2. 特殊情况可以重写 update 和 create
class BookSerializer(serializers.ModelSerializer):
# ★★★★ 也可以这样重写字段, 括号 里面的校验规则优先级大于 extra_kwargs 中的优先级, 会导致后者失效。
name = serializers.CharField()
# 局部校验钩子
def validate_name(self, name):
pass
# 全局校验钩子
def validate(self, attrs):
pass
# 重写字段
publish = serializers.SerializerMethodField(read_only=True) # 只用来做序列化
def get_publish(self, obj):
pass
"""缩进关系搞懂"""
class Meta:
model = Book # 跟Book做对应
# fields='__all__' # 序列化和反序列化Book中的所有字段
# ★★★★ 无论模型表中的方法, 还是序列化类中的方法, 都需要映射。
fields = ['name', 'price', 'publish'] # 只序列化和反序列化某几个字段(映射)
# 在 extra_kwargs 中写校验规则, 和 Serializer 中直接写在括号中一样。
extra_kwargs = {
'name': {'max_length': 8, 'min_length': 3},
'price': {'max_value': 88},
# 只读(只做序列化)
'publish': {'read_only': True}
# 伪代码
'要校验的字段名': {'校验名': '校验值'}
}
六. Serializer 和 ModelSerializer 区别
# 伪代码展示
class MySerializer(Serializer or ModelSerializer):
"""-------------关系映射-------------"""
# Serializer
name = serializers.CharField()
price = serializers.CharField()
# 重写字段
publish = serializers.SerializerMethodField(read_only=True)
def get_publish(self, obj):
pass
# ModelSerializer
class Meta:
model = Book
fields = ['name', 'price', 'publish'] # 这里要映射
# 重写字段
publish = serializers.SerializerMethodField(read_only=True)
def get_publish(self, obj):
pass
"""-------------update create 方法-------------"""
# Serializer
需要重写, 因为源码没有这两个方法。
# ModelSerializer
不需要重写, 必要时可以重写。
七. 多表序列化(重点)
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()),
]
from rest_framework.response import Response
from rest_framework.views import APIView
from app01 import models
from app01 import serialize
class BookView(APIView):
def get(self, request):
books = models.Book.objects.all()
res = serialize.MySerialize(instance=books, many=True)
print(res.data)
return Response(res.data)
def post(self, request):
print(request.data)
res = serialize.MySerialize(data=request.data)
if res.is_valid():
res.save()
return Response({"msg": "新增成功!", "code": 100})
else:
return Response({"msg": "新增失败!", "code": 101, "err": res.errors})
class BookDetailView(APIView):
def get(self, request, pk):
book = models.Book.objects.filter(pk=pk).first()
res = serialize.MySerialize(instance=book)
print(res.data)
return Response(res.data)
def put(self, request, pk):
book = models.Book.objects.filter(pk=pk).first()
res = serialize.MySerialize(instance=book, data=request.data)
if res.is_valid(): # 会执行:序列化类字段自己的校验规则,局部钩子,全局钩子
res.save() # 数据保存
return Response({"msg": "修改成功!", "code": 100})
else:
return Response({"msg": "修改失败!", "code": 101, "err": res.errors})
from rest_framework import serializers
from .models import Book
class MySerialize(serializers.ModelSerializer):
class Meta:
model = Book
# fields = '__all__'
fields = ('nid', 'name', 'price', 'publish', 'authors', 'publish_detail', 'author_list')
depth = 0
extra_kwargs = {
"publish": {'write_only': True, 'read_only': False},
"authors": {'write_only': True, 'read_only': False}
}
publish_detail = serializers.SerializerMethodField(read_only=True)
def get_publish_detail(self, obj):
return {'name': obj.publish.name, 'city': obj.publish.city}
author_list = serializers.SerializerMethodField(read_only=True)
def get_author_list(self, obj):
author_list = []
for author in obj.authors.all():
author_list.append({'name': author.name, 'addr': author.author_detail.addr})
return author_list
八. 重写字段方法
方式一
重写字段 + 必须配合一个方法。 方法返回啥,该字段就是什么--->该字段只能序列化(只出不进)
# 3. 在 fields 里注册
fields=["publish_detail"]
# 2. 配和方法写字段
publish_detail=serializers.SerializerMethodField(read_only=True)
# 1. 重写一个方法
def get_publish_detail(self,obj):
# obj就是当前 book对象
print(obj)
# 返回一个字段
return obj.publish.name"""
# 返回多个字段
return {'name':obj.publish.name,'city':obj.publish.city}
方式二
在表模型models中写方法,在序列化类中写到fields中
序列化类中:
"""fields"""
fields=['publish_detail']
模型层:
class Book(models.Model):
nid = models.AutoField(primary_key=True)
name = models.CharField(max_length=32)
price = models.DecimalField(max_digits=5, decimal_places=2)
......
"""方法 1, (普通字段)"""
def publish_detail(self):
return {"name": self.publish.name, "email": self.publish.email} # 还回字段可以多样性
"""方法 2, (选择字段)"""
# 还回选择序号对应的字段中文名
def publish_detail(self):
return self.get_数据库已有的字段名_display()
方式三
对某个字段重写序列化类--(子序列化)
class TeacherSerializer(serializers.ModelSerializer):
class Meta:
model = Teacher
# fields = '__all__'
fields = ['id','name','role','title','signature','image','brief']
class CourseSerializer(serializers.ModelSerializer):
# 子序列化:通过老师的序列化类来实现序列化
teacher=TeacherSerializer()
class Meta:
model = Course
fields = [
...
'teacher'
...
]