序列化组件
一 序列化组件介绍
是drf提供的一个类,from rest_framework import serializers,继承它后写自己的类,可以序列化queryset或者单个对象。
二 序列化组件基本使用
models.py
from django.db import models
# 创建员工表
class Employee(models.Model):
name = models.CharField(max_length=32)
department = models.CharField(max_length=32)
phone = models.CharField(max_length=32)
创建一个py文件用于写序列化组件类,这里叫emp_serializer.py
from rest_framework import serializers
from app01.models import Employee
class SerializersEmp(serializers.Serializer):
name = serializers.CharField()
department = serializers.CharField()
phone = serializers.CharField()
views.py
from app01.emp_serializer import SerializersEmp
# 通过序列化组件
class EmployeeView(APIView):
# 获取所有员工信息
def get(self, request):
query_set = Employee.objects.all()
# 序列化多个需要指定参数many=True
ser = SerializersEmp(instance=query_set, many=True)
return Response(ser.data)
def post(self, request):
# 新增员工数据
# 将数据反序列化
ser = SerializersEmp(data=request.data)
if ser.is_valid(): # 判断数据是否合法
ser.save() # 调用save方法 没有instance参数就是创建
# 需要在序列化组件中重写create方法
return Response(ser.data)
return Response(ser.errors)
class EmployeeViewDetail(APIView):
# 获取单个
def get(self, request, pk):
emp_obj = Employee.objects.filter(pk=pk).first()
ser = SerializersEmp(instance=emp_obj)
return Response(ser.data)
# 修改
def put(self, request, pk):
emp_obj = Employee.objects.filter(pk=pk).first()
ser = SerializersEmp(instance=emp_obj, data=request.data)
if ser.is_valid():
ser.save() # 用instance参数是修改,需要重写序列化组件中的update方法
return Response(ser.data)
return Response(ser.errors)
# 修改
def patch(self, request, pk):
emp_obj = Employee.objects.filter(pk=pk).first()
ser = SerializersEmp(instance=emp_obj, data=request.data)
if ser.is_valid():
ser.save()
return Response(ser.data)
return Response(ser.errors)
def delete(self, request, pk):
Employee.objects.filter(pk=pk).delete()
return Response() # 返回空
urls.py
from app01.views import EmployeeView, EmployeeViewDetail
urlpatterns = [
path('employee/', EmployeeView.as_view()),
path('employee/<int:pk>/', EmployeeViewDetail.as_view()),
]
SerializersEmp类中的create方法
def create(self, validated_data):
# 将校验过的数据写入表中
Employee.objects.create(**validated_data)
# 返回写入的对象
return validated_data
SerializersEmp类中的update方法
def update(self, instance, validated_data):
instance.name = validated_data.get('name')
instance.department = validated_data.get('department')
instance.phone = validated_data.get('phone')
instance.save()
return instance
练习
fbv写—》写个装饰器,装饰在视图函数,只要一装饰,以后的request就可以使用request.data,这个data无论是那种编码格式,都有数据。
def decorate(func_name):
def inner(request, *args, **kwargs):
if request.content_type == 'multipart/form-data':
request.data = request.POST
if request.content_type == 'application/x-www-form-urlencoded':
request.data = request.POST
if request.content_type == 'application/json':
import json
request.data = json.loads(request.body)
res = func_name(request, *args, **kwargs)
return res
return inner
@decorate
def homework(request):
# application/json multipart/form-data
# application/x-www-form-urlencoded
print(request.data)
return HttpResponse('ok')
路由
path('homework/', views.homework),
三 序列化类常用字段类和字段参数
3.1 常用字段类
# 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)
# 常用
# CharField
# BooleanField
# IntegerField
# DecimalField
# ListField
# DictField
3.2 常用字段参数
选项参数:
参数名称 作用
# 给CharField字段类使用的参数
max_length 最大长度
min_lenght 最小长度
allow_blank 是否允许为空
trim_whitespace 是否截断空白字符
# 给IntegerField、DecimalField字段类使用的参数
max_value 最小值
min_value 最大值
通用参数:
参数名称 说明
required 表明该字段在反序列化时必须输入,默认True
default 反序列化时使用的默认值
allow_null 表明该字段是否允许传入None,默认False
validators 该字段使用的验证器
error_messages 包含错误编号与错误信息的字典
label 用于HTML展示API页面时,显示的字段名称
help_text 用于HTML展示API页面时,显示的字段帮助提示信息
# 重点
read_only 表明该字段仅用于序列化输出,默认False
write_only 表明该字段仅用于反序列化输入,默认False
四 序列化高级用法source
-source指定的可以是字段,也可以是方法,用于重命名
-source可以做跨表查询
class BookSerializer(serializers.Serializer):
book_name = serializers.CharField(max_length=8, min_length=3,source='name')
publish_name = serializers.CharField(max_length=8, min_length=3,source='publish.name')
# xx表示模型类中的方法 会自动加括号执行
xx = serializers.CharField(max_length=8, min_length=3,source='xx')
五 定制序列化字段的两种方式
5.1 在序列化类中写SerializerMethodField
xxx = serializers.SerializerMethodField()
# obj是当前序列化类的对象
def get_xxx(self, obj):
return {'name': obj.name}
# 获取书籍对象的所有作者
author = serializers.SerializerMethodField()
def get_author(self, book_obj):
return [{'name': obj.name, 'age': obj.age, 'phone': obj.author_detail.phone} for obj in book_obj.author.all()]
5.2 在模型类中定义方法
class Book(models.Model):
name = models.CharField(max_length=32)
price = models.DecimalField(max_digits=6, decimal_places=2)
create_time = models.DateField()
publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE)
author = models.ManyToManyField(to='Author')
def __str__(self):
return f'{self.name}'
# 可以加property装饰 也可以不加 序列化组件可以不用加括号拿到
@property
def publish_detail(self):
return {'name': self.publish.name, 'city': self.publish.city, 'email': self.publish.email}
def author_list(self):
return [{'name': obj.name, 'age': obj.age, 'phone': obj.author_detail.phone} for obj in self.author.all()]
5.3 有关联关系表数据创建
# 只写不读 只反序列化不序列化
publish = serializers.IntegerField(write_only=True)
author = serializers.ListField(write_only=True)
# 只读不写 只序列化不反向序列化
publish_detail = serializers.DictField(read_only=True)
author_list = serializers.ListField(read_only=True)
# 继承serializers.Serializer一定要重写creata方法和update方法
def create(self, validated_data):
name = validated_data.get('name')
price = validated_data.get('price')
create_time = validated_data.get('create_time')
publish = validated_data.get('publish')
author = validated_data.get('author')
# 创建书籍数据
obj = Book.objects.create(name=name, price=price, create_time=create_time, publish_id=publish)
# 添加书籍和作者关系 多对多
obj.author.add(*author)
return obj
def update(self, instance, validated_data):
name = validated_data.get('name')
price = validated_data.get('price')
create_time = validated_data.get('create_time')
publish = validated_data.get('publish')
author = validated_data.get('author')
instance.name = name
instance.price = price
instance.create_time = create_time
instance.publish_id = publish
instance.author.set(author)
instance.save()
return instance
总结:继承Serializer的序列化组件需要写校验的每个字段,创建和修改数据时,需要重写create和update方法。而使用ModelSerializer,不需要写这些。
六 反序列化之数据校验
首先是校验字段自己的校验规则,通过后校验局部钩子,最后校验全局钩子。
6.1 字段自己的校验规则
class BookSerializer(serializers.Serializer):
# 继承Serializer字段自己的校验规则
book_name = serializers.CharField(max_length=8, min_length=3, required=True,)
class BookModelSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = ['name', 'price', 'create_time', 'publish', 'author', 'publish_detail', 'author_list']
extra_kwargs = {
#继承ModelSerializer字段自己的校验规则
'name': {'max_length': 8, 'min_length': 3},
}
6.2 局部钩子
def validate_name(self, name):
if 'sb' in name:
raise ValidationError('名字不能包含敏感词')
return name
def validate_price(self, price):
if price == 66:
raise ValidationError('价格不能是66')
return price
6.3 全局钩子
def validate(self,attrs):
if attrs.get('password') != attrs.get('re_password'):
raise ValidationError('两次密码不一致')
return attrs
七 模型类序列化器(ModelSerializer)的使用
步骤:
- 定义一个序列化组件类,继承ModelSerializer。
- 在序列化组件类中定义一个Meta类,类中指定model和fields参数。
- 在Meta类中指定extra_kwargs,给字段添加参数。
- 在序列化类中,可以重写某个字段,优先使用你重写的。
- 不需要再重写create和update方法。
class BookModelSerializer(serializers.ModelSerializer):
class Meta:
model = Book
# fields = ['__all__'] 所有字段都序列化
fields = ['name', 'price', 'create_time', 'publish', 'author', 'publish_detail', 'author_list']
extra_kwargs = {
'name': {'max_length': 8, 'min_length': 3},
'price': {'min_value': 9, 'max_value': 199},
'publish': {'write_only': True},
'author': {'write_only': True},
'publish_detail': {'read_only': True},
'author_list': {'read_only': True},
}
def validate_name(self, name):
if 'sb' in name:
raise ValidationError('名字不能包含敏感词')
return name
def validate_price(self, price):
if price == 66:
raise ValidationError('价格不能是66')
return price
八 反序列化数据校验源码分析
入口是序列化组件对象.is_valid()方法。
BaseSerializer里的is_valid
def is_valid(self, *, raise_exception=False):
if not hasattr(self, '_validated_data'):
try:
# 真正开始校验 如果校验成功返回校验通过的数据
self._validated_data = self.run_validation(self.initial_data)
except ValidationError as exc:
self._validated_data = {}
self._errors = exc.detail
else:
self._errors = {}
if self._errors and raise_exception:
raise ValidationError(self.errors)
return not bool(self._errors)
执行self.run_validation(self.initial_data)
Serializer里的run_validation
def run_validation(self, data=empty):
# 执行局部钩子
value = self.to_internal_value(data)
try:
# 执行全局钩子
self.run_validators(value)
value = self.validate(value)
assert value is not None, '.validate() should return the validated data'
except (ValidationError, DjangoValidationError) as exc:
raise ValidationError(detail=as_serializer_error(exc))
return value
def to_internal_value(self, data):=
for field in fields:
# 反射判断是否有局部钩子函数
validate_method = getattr(self, 'validate_' + field.field_name, None)
primitive_value = field.get_value(data)
try:
# 字段本身的校验规则
validated_value = field.run_validation(primitive_value)
# 局部钩子校验
if validate_method is not None:
validated_value = validate_method(validated_value)
except ValidationError as exc:
errors[field.field_name] = exc.detail
except DjangoValidationError as exc:
errors[field.field_name] = get_error_detail(exc)
except SkipField:
pass
else:
set_value(ret, field.source_attrs, validated_value)
if errors:
raise ValidationError(errors)
return ret
练习
写出book表(带关联关系)5 个接口
# urls.py
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.BookViewV2.as_view()),
]
# models.py
from django.db import models
# Create your models here.
class Book(models.Model):
name = models.CharField(max_length=32)
price = models.DecimalField(max_digits=6, decimal_places=2)
create_time = models.DateField()
publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE)
author = models.ManyToManyField(to='Author')
def __str__(self):
return f'{self.name}'
# 可以加property装饰 也可以不加 序列化组件可以不用加括号拿到
@property
def publish_detail(self):
return {'name': self.publish.name, 'city': self.publish.city, 'email': self.publish.email}
def author_list(self):
return [{'name': obj.name, 'age': obj.age, 'phone': obj.author_detail.phone} for obj in self.author.all()]
class Publish(models.Model):
name = models.CharField(max_length=32)
city = models.CharField(max_length=32)
email = models.EmailField()
def book_list(self):
return [{'name': obj.name, 'price': obj.price} for obj in self.book_set.all()] # 反向查询结果有多个表名小写加_set
def __str__(self):
return f'{self.name}'
class Author(models.Model):
name = models.CharField(max_length=32)
age = models.IntegerField()
author_detail = models.OneToOneField(to='AuthorDetail', on_delete=models.CASCADE)
def __str__(self):
return f'{self.name}'
@property
def detail(self):
return {'phone': self.author_detail.phone, 'birthday': self.author_detail.birthday,
'addr': self.author_detail.addr}
class AuthorDetail(models.Model):
phone = models.CharField(max_length=32)
birthday = models.DateField()
addr = models.CharField(max_length=32)
def aut(self):
return {'name': self.author.name, 'age': self.author.age}
# serializer.py
from rest_framework import serializers
from .models import *
from rest_framework.exceptions import ValidationError
# ModelSerializer(简单,不用重写create和update)
# name最大8,最小3,名字中不能带sb
# price最小9,最大199,不能为66
# 基于ModelSerializer
class BookModelSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = ['name', 'price', 'create_time', 'publish', 'author', 'publish_detail', 'author_list']
extra_kwargs = {
'name': {'max_length': 8, 'min_length': 3},
'price': {'min_value': 9, 'max_value': 199},
'publish': {'write_only': True},
'author': {'write_only': True},
'publish_detail': {'read_only': True},
'author_list': {'read_only': True},
}
def validate_name(self, name):
if 'sb' in name:
raise ValidationError('名字不能包含敏感词')
return name
def validate_price(self, price):
if price == 66:
raise ValidationError('价格不能是66')
return price
# 基于Serializer
# name最大8,最小3,名字中不能带sb
# price最小9,最大199,不能为66
class BookSerializer(serializers.Serializer):
name = serializers.CharField(max_length=8, min_length=3, required=True,
error_messages={
'max_length': '名字最长8位',
'min_length': '名字最短3位',
'required': '名字必填'
})
price = serializers.DecimalField(max_value=199, min_value=9, required=True, max_digits=6, decimal_places=2,
error_messages={
'max_value': '最大价格不能超过199',
'min_value': '价格不能小于9'
})
# xxx = serializers.SerializerMethodField()
#
# def get_xxx(self, obj):
# return {'name': obj.name}
create_time = serializers.DateField()
publish = serializers.IntegerField(write_only=True)
author = serializers.ListField(write_only=True)
publish_detail = serializers.DictField(read_only=True)
author_list = serializers.ListField(read_only=True)
def validate_name(self, name):
if 'sb' in name:
raise ValidationError('名字不能包含敏感词')
return name
def validate_price(self, price):
if price == 66:
raise ValidationError('价格不能是66')
return price
def create(self, validated_data):
name = validated_data.get('name')
price = validated_data.get('price')
create_time = validated_data.get('create_time')
publish = validated_data.get('publish')
author = validated_data.get('author')
# 创建书籍数据
obj = Book.objects.create(name=name, price=price, create_time=create_time, publish_id=publish)
# 添加书籍和作者关系 多对多
obj.author.add(*author)
return obj
def update(self, instance, validated_data):
name = validated_data.get('name')
price = validated_data.get('price')
create_time = validated_data.get('create_time')
publish = validated_data.get('publish')
author = validated_data.get('author')
instance.name = name
instance.price = price
instance.create_time = create_time
instance.publish_id = publish
instance.author.set(author)
instance.save()
return instance
# views.py
from rest_framework.views import APIView
from .models import *
from rest_framework.response import Response
from .serializer import BookModelSerializer
from .serializer import PublishModelSerializer, AuthorModelSerializer, DetailModelSerializer
# 基于ModelSerializer进行序列化和反序列化
# class BookView(APIView):
# # 获取全部
# def get(self, request):
# query_set = Book.objects.all()
# ser = BookModelSerializer(instance=query_set, many=True)
# return Response(ser.data)
#
# # 新增一条
# def post(self, request):
# # 反序列化
# ser = BookModelSerializer(data=request.data)
# if ser.is_valid():
# # 使用ModelSerializer不用重写create方法
# ser.save()
# return Response({'code': 100, 'msg': '图书新增成功'})
# return Response(ser.errors)
#
#
# class BookViewV2(APIView):
# def get(self, request, pk):
# obj = Book.objects.filter(pk=pk).first()
# ser = BookModelSerializer(instance=obj)
# return Response(ser.data)
#
# def put(self, request, pk):
# obj = Book.objects.filter(pk=pk).first()
# ser = BookModelSerializer(instance=obj, data=request.data)
# if ser.is_valid():
# # 不用再重写update方法
# ser.save()
# return Response(ser.data)
# return Response(ser.errors)
#
# def delete(self, request, pk):
# Book.objects.filter(pk=pk).delete()
# return Response({})
# 基于Serializer序列化组件进行序列化和反序列化
from .serializer import BookSerializer
class BookView(APIView):
def get(self, request):
query_set = Book.objects.all()
ser = BookSerializer(instance=query_set, many=True)
return Response(ser.data)
def post(self, request):
ser = BookSerializer(data=request.data)
if ser.is_valid():
# 需要重写序列化组件的create方法
ser.save()
return Response(ser.data)
return Response(ser.errors)
class BookViewV2(APIView):
def get(self, request, pk):
obj = Book.objects.filter(pk=pk).first()
ser = BookSerializer(instance=obj)
return Response(ser.data)
def put(self, request, pk):
obj = Book.objects.filter(pk=pk).first()
ser = BookSerializer(instance=obj, data=request.data)
if ser.is_valid():
# 需要重写序列化组件的update方法
ser.save()
return Response(ser.data)
return Response(ser.errors)
def delete(self, request, pk):
Book.objects.filter(pk=pk).delete()
return Response({})