drf之序列化组件
drf之序列化组件
序列化与反序列化器
序列化器介绍和快速使用
- 在写接口时,需要序列化和反序列化,而且反序列化的过程中要做数据校验,drf直接提供了固定的写法,只需要按照固定写法使用,就能完成上面的三个需求。
- 提供了两个类
Serializer
ModelSerializer
,编写自定义的类,只需要继承drf提供的序列化类,就可以使用其中的某些方法,也能完成上面的三个需求
基于序列化器劣化和反序列化
序列化类的作用:做序列化,反序列化,反序列化校验
序列化过程:
- 写一个序列化类,继承
Serializer
,编写每个字段CharField
,字段表示要序列化和反序列化的字段 - 视图类中:导入
Book
,实例化得到对象books
,对查出来的query对象们
,对单个对象序列化并传入instance
=books
参数,如果是query是复数,一定要传many=True
,query是单个对象,就不需要传入many - 序列化类对象 :
ser.data ``----> 字典或列表---通过
Resonse`将json格式字符串返给前端 - 序列化单条和序列化多条,因为序列化单条需要传主键(
PK
),分别书写了两个路由,对应了两个视图类和两个get
方法
序列化的基本使用
-
序列化多条
serializer.py--BookSerializer类
from rest_framework import serializers class BookSerializer(serializers.Serializer): # 序列化某些字段,这里写要序列化的字典 name = serializers.CharField() # serializers下大致跟models下的类是对应的 price = serializers.CharField() publish = serializers.CharField()
如果只是想序列化某几字段,就把不需要序列化的字段注释掉即可
views.py--->BookView类
class BookView(APIView): def get(self, request): # 验证原生request和新生的request print(request.method) # GET print(request._request) # <WSGIRequest: GET '/books/'> print(type(self.request)) # <class 'rest_framework.request.Request'> books = Book.objects.all() # 使用序列化类来完成---》得有个序列化类 # instance要序列化的数据books queryset对象 # many=True 只要是queryset对象要传many=True,如果是单个对象就不用传 ser = BookSerializer(instance=books, many=True) return Response(ser.data) # 无论是列表还是字典都可以序列化
-
序列化单条
序列化类--默认
from rest_framework import serializers class BookSerializer(serializers.Serializer): # 序列化某些字段,这里写要序列化的字典 name = serializers.CharField() price = serializers.CharField() publish = serializers.CharField()
views.py--->BookDetailView类
class BookDetailView(APIView): # def get(self, request,pk): # 与下列方法时一样的, def get(self, request, *args, **kwargs): # pk是以关键字传入的 book = Book.objects.filter(pk=kwargs.get('pk')).first() # 序列化 ser = BookSerializer(instance=book) return Response(ser.data)
url 路由
添加了新的路由
urlpatterns = [ path('books/<int:pk>/',views.BookDetailView.as_view()) ]
反序列化基本使用
反序列化过程:新增,修改
新增:
1. 前端传入后端的数据,不论编码格式,都在request.data中,request.data
格式是字典
前端根据传入的编码格式不一样,从request.data取到的字典形式也是不一样的
编码格式 字典
urlencoded QueryDict
form-data QueryDict
json dict
2. 将前端传入的数据request.data进行反序列化,并完成序列化类的反序列化
3. 序列化类得到对象并传入参数:data=request.data
校验数据
保存:ser.save()--->序列化类中重写create方法
修改:
1. 拿到前端传入的数据,进行反序列化,查出要修改的对象--->序列化类的反序列化
2. 序列化类得到对象,传入参数:instance=要修改的对象,data=request.data
校验数据
保存:ser.save() --->序列化类中重写update方法
-
序列化的新增
序列化类
class BookSerializer(serializers.Serializer): # 序列化某些字段,这里写要序列化的字典 name = serializers.CharField() price = serializers.CharField() publish = serializers.CharField() # 新增一条数据 def create(self, validated_data): # 保存的逻辑 # validated_data 校验过后的数据 {name,price,publish} # 保存到数据库 book = Book.objects.create(**validated_data) # 一定不要忘记返回新增的对象 return book
视图类
class BookView(APIView): def post(self, request): # requset.data # 前端提交要保存的数据-->校验数据-->存数据 ser = BookSerializer(data=request.data) # 把前端传入的要保存的数据,给data参数 # 校验数据 if ser.is_valid(): # 保存-->需要自己写,要在序列化类BookSerializer中写-->create方法 ser.save() # 调用ser.save,自动触发自定义编辑create方法保存数据 return Response({'code': 100, 'msg': '新增成功', 'result': ser.data}) else: return Response({'code': 101, 'msg': ser.errors}) 注释: print(ser) """BookSerializer(data=<QueryDict: {'name': ['cc'], 'price': ['123'], 'publish': ['上海']}>): name = CharField() price = CharField() publish = CharField() """ print(ser.data) # {'name': 'cc', 'price': '123', 'publish': '上海'}
-
反序列化的修改
序列化类
class BookSerializer(serializers.Serializer): # 序列化某些字段,这里写要序列化的字典 name = serializers.CharField() price = serializers.CharField() publish = serializers.CharField() def create(self, validated_data): book = Book.objects.create(**validated_data) return book # 修改对象 def update(self, instance, validated_data): # instance 要修改的对象 # validated_data 校验过后的数据 instance.name = validated_data.get('name') instance.price = validated_data.get('price') instance.publish = validated_data.get('publish') instance.save() # orm的单个对象,修改了单个对象的属性,只要调用对象.save,就能把修改保存到数据库 return instance # 记得把修改的对象返回
视图类
class BookDetailView(APIView): def put(self, request, pk): book = Book.objects.filter(pk=pk).first() # 反序列化保存 ---借助于序列化类 ser = BookSerializer(data=request.data, instance=book) if ser.is_valid(): ser.save() # 由于没有重写update,所以这报错 return Response({'code': 100, 'msg': '修改成功', 'result': ser.data}) else: return Response({'code': 101, 'msg': ser.errors})
-
删除单条
视图类
class BookDetailView(APIView): def delete(self,request,pk): Book.objects.filter(pk=pk).delete() return Response({'code':100,'msg':'删除成功'})
基于序列化器编写5个接口(重点)
url
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())
]
models.py
from django.db import models
class Book(models.Model):
name = models.CharField(max_length=32,verbose_name='书名')
price = models.CharField(max_length=32,verbose_name='价格')
publish = models.CharField(max_length=32,verbose_name='出版社')
serializer.py
from rest_framework import serializers
from .models import Book
# 多条
class BookSerializer(serializers.Serializer):
# 序列化某些字段,这里泻药序列化的字典
name = serializers.CharField()
price = serializers.CharField()
publish = serializers.CharField()
# 新增一条数据
def create(self, validated_data):
# 保存的逻辑
# validated_data 校验过后的数据 {name,price,publish}
# 保存到数据库
book=Book.objects.create(**validated_data)
# 一定不要忘记返回新增的对象
return book
# 修改数据
def update(self, instance, validated_data):
# instance 要修改的对象
# validated_data 校验过后的数据
instance.name=validated_data.get('name')
instance.price=validated_data.get('price')
instance.publish=validated_data.get('publish')
instance.save() # orm的单个对象,修改了单个对象的属性,只要调用对象.save,就能把修改保存到数据库
return instance # 不要忘了把修改后的对象,返回
view.py
from rest_framework.views import APIView,Response
from .models import Book
from django.http import JsonResponse
from django.core.handlers.wsgi import WSGIRequest
from rest_framework.request import Request
# 序列化多条
from .serializer import BookSerializer
class BookView(APIView):
def get(self,request):
# 只是为了验证之前讲过的
print(request.method)
print(request._request)
print(type(self.request))
books = Book.objects.all()
# 使用自定义的序列化类来完成
# instance要序列化的数据books queryset对象
# many=True 只要是queryset对象要传many=True,如果是单个对象就不用传
ser = BookSerializer(instance=books,many=True)
return Response(ser.data)
# 反序列化---新增
def post(self,request):
# requset.data # 前端提交要保存的数据-->校验数据-->存数据
ser = BookSerializer(data=request.data)
# 检验数据
if ser.is_valid():
# 保存-->需要自己写,要在序列化类BookSerializer中写-->create方法
ser.save() # 调用ser.save,自动触发自定义编辑create方法保存数据
# print(ser)
"""BookSerializer(data=<QueryDict: {'name': ['cc'], 'price': ['123'], 'publish': ['上海']}>):
name = CharField()
price = CharField()
publish = CharField()
"""
# print(ser.data) # {'name': 'cc', 'price': '123', 'publish': '上海'}
print(ser.errors)
return Response({'code':100,'msg':'新增成功','result':ser.data})
else:
return Response({'code':101,'msg':ser.errors})
# 序列---单条
from .serializer import BookSerializer
class BookDetailView(APIView):
def get(self,request,*args,**kwargs):
book = Book.objects.filter(pk=kwargs.get('pk')).first()
# 序列化
ser =BookSerializer(instance=book)
return Response(ser.data)
# 反序列化---修改数据
def put(self,request,pk):
book = Book.objects.filter(pk=pk).first()
ser = BookSerializer(data=request.data,instance=book)
if ser.is_valid():
ser.save() # # 由于没有重写update,所以这报错
return Response({'code':100,'msg':'修改成功','result':ser.data})
else:
return Response({'code':101,'msg':ser.errors})
# 反序列化---删除数据
def delete(self,request,pk):
Book.objects.filter(pk=pk).delete()
return Response({'code':100,'msg':'删除成功'})
反序列化的校验
序列化类反序列化的数据校验功能类比forms组件
- 局部钩子
- 全局钩子
代码实现:
class BookSerializer(serializers.Serializer):
name = serializers.CharField()
price = serializers.CharField()
publish = serializers.CharField()
# 新增一条数据
def create(self, validated_data):
book = Book.objects.create(**validated_data)
return book
# 修改一条数据
def update(self, instance, validated_data):
instance.name = validated_data.get('name')
instance.price = validated_data.get('price')
instance.publish = validated_data.get('publish')
instance.save()
return instance # 保存
# 反序列化校验的局部钩子,名字不能以h开头
def validate_name(self,name):
# 校验name是否合法
if name.startswith('h'):
# 校验不通过,抛异常
raise ValidationError('不能以h开头')
else:
return name
# 全局钩子
def validate(self,attrs):
# 校验过后的数据,书名和出版社的名字不能一致
if attrs.get('name')==attrs.get('publish'):
raise ValidationError('书名和出版社的名字不能一致')
else:
return attrs
序列化常用字段和字段参数(了解)
常用字段类
重点记忆是加粗字段
字段名 | 字段参数 |
---|---|
CharField | max_length=None, min_length=None, allow_blank=False, trim_whitespace=True |
IntegerField | max_value=None, min_value=None |
FloatField | max_value=None, min_value=None |
BooleanField | |
NullBooleanField | |
FloatField | max_value=None, min_value=None |
DecimalField | max_digits, decimal_places, coerce_to_string=None, max_value=None, min_value=None 注:max_digits: 最多位数 decimal_palces: 小数点位置 |
TimeField | format=api_settings.TIME_FORMAT, input_formats=None |
DateField | format=api_settings.DATE_FORMAT, input_formats=None |
DateTimeField | format=api_settings.DATETIME_FORMAT, input_formats=None) |
EmailField | max_length=None, min_length=None, allow_blank=False |
RegexField | regex, max_length=None, min_length=None, allow_blank=False |
SlugField | max_length=50, min_length=None, allow_blank=False),正则字段,验证正则模式 [a-zA-Z0-9-]+ |
URLField | max_length=200, min_length=None, allow_blank=False |
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 | protocol=’both’, unpack_ipv4=False, **options) |
DurationField | |
ChoiceField | choices,choices与Django的用法相同 |
MultipleChoiceField | choices |
FileField | max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL |
ImageField | max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL |
ListField | child=, min_length=None, max_length=None |
DictField | child= |
常用字段参数
CharField及其子类的(EmailField)
反序列化的校验,字段自己的规则
参数 | 含义 |
---|---|
max_length | 最大长度 |
min_lenght | 最小长度 |
allow_blank | 是否允许为空 |
trim_whitespace | 是否截断空白字符 |
IntegerField
参数 | 含义 |
---|---|
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(了解)
首先先创建一个django项目drf_day04
,创建Book
表、Publish
表以及Author
表,并建立三个表之间的关系,完成模型表数据的迁移并录入数据
from django.db import models
# 创建关联表
class Book(models.Model):
name = models.CharField(max_length=32)
price = models.CharField(max_length=32)
# 外键 书与出版社 -对多,关联字段写在多的一方,写在Book
publish=models.ForeignKey(to='Publish',on_delete=models.CASCADE)
# 书与作者 多对多 需要建立中间表,django自动生成第三张表
authors = models.ManyToManyField(to='Author')
class Publish(models.Model):
name= models.CharField(max_length=32)
addr= models.CharField(max_length=32)
class Author(models.Model):
name = models.CharField(max_length=32)
phone = models.CharField(max_length=11)
序列化定制字段名字
sourse
- 可以定制序列化字段名
- 防止数据被人篡盗,将前端展示的字段名和后端数据的字段名设置成不同的字段名
sourse序列化自有字段和关联字段的区别
1.自有字段,直接写表字段名
real_name = serializers.CharField(max_length=8,source='name')
2.关联字段 通过外键获取
一对多 一对多的关联,直接点
publish=serializers.CharField(max_length=8,source='publish.name')
多对多 source不能用实现定制序列化关联表的字段
authors = serializers.CharField(source='authors.all')
代码展示:
视图类:
class BookAPIView(APIView):
def get(self,request):
books = Book.objects.all()
ser = BookSerializer(instance=books,many=True)
return Response(ser.data)
定制序列化类
from rest_framework import serializers
# 序列化类
class BookSerializer(serializers.Serializer):
# 字段参数 都可以通过sourse定制具体的字段名
# 自有字段,直接写表字段名
real_name = serializers.CharField(max_length=8,source='name')
real_price = serializers.CharField(source='price')
# 关联字段 一对多 ,直接通过外键点
publish=serializers.CharField(max_length=8,source='publish.name')
# 多对多,source不能用定制关联表的字段
authors = serializers.CharField(source='authors.all')
"""
1.我们序列化的是book表字段,自有字段名直接通过sourse指定的字段名-->>是Book表的字段名
2.sourse定制目标的字段名name(max_length=8)...
3.提高了安全性,后端真实的字段名:name、price,
序列化给前端字段名:real_name、real_price,"authors": "<QuerySet [<Author: Author object (1)>, <Author: Author object (2)>]>"
"""
序列化高级用法之定制字段的两种方式
定制关联字段(publish/authors
)的显示形式
-
一对多显示字典:{}
-
多对多显示列表套字典:[{},{},{}]
SerializerMethodField定制
返回给前端格式
{
"name": "红楼梦",
"price": "11",
"publish_detail": {"name": "北京出版社","addr": "北京"},
"author_list": [{"name": "吴承恩","phone": "110"},{"name": "曹雪芹","phone": "120"}]
}
高级序列化之SerializerMethodField
class BookSerializer(serializers.Serializer):
name = serializers.CharField(max_length=8)
price = serializers.CharField()
# 定制返回格式----SerializerMethodField
publish_detail = serializers.SerializerMethodField()
@propety # 可以伪装为方法的数据属性
def get_publish_detail(self, obj):
print(obj)
return {'name': obj.publish.name, 'addr': obj.publish.addr}
author_list = serializers.SerializerMethodField()
@propety # 可以伪装为方法的数据属性
def get_author_list(self, obj):
l1 = []
for author in obj.authors.all():
l1.append({'name': author.name, 'phone': author.phone})
return list
在表模型中定制
返回给前端格式
{
"name": "红楼梦",
"price": "11",
"publish_detail": {"name": "北京出版社","addr": "北京"},
"author_list": [{"name": "吴承恩","phone": "110"},{"name": "曹雪芹","phone": "120"}]
}
1.表模型
class Book(models.Model):
name = models.CharField(max_length=32)
price = models.CharField(max_length=32)
publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE)
authors = models.ManyToManyField(to='Author')
# 定制在模型类中的方法
def publish_detail(self):
return {'name': self.publish.name, 'addr': self.publish.addr}
def author_list(self):
l1 = []
for author in self.authors.all():
l1.append({'name': author.name, 'phone': author.phone})
return l1
2.序列化类
# 定制在模型类
class BookSerializer(serializers.Serializer):
name = serializers.CharField(max_length=8)
price = serializers.CharField()
# publish_detail = serializers.CharField()
publish_detail = serializers.DictField()
author_list = serializers.ListField()
总结
- 在序列化类中继承
SerializerMethodField
,类中编写一个get_字段名的
(get_publish_detail)方法,该方法返回值=该字段值,该字段只能做序列化字段,反序列化不行 - 在表模型中使用编写:
get_字段名的
(get_publish_detail)方法,该publish_detail
等于方法返回的值,序列化类中需要配合ListField,DictField两个字段类型---->该字段只能做序列化使用,不能实现做反序列化的字段 - 可以将上述的方法可以包装成数据属性---使用伪装@property
多表关联反序列化保存
##反序列化校验执行流程
1.先执行自有字段的校验(参数的控制)--->最大长度,最小长度,是否为空,是否必填,最小数字
2.validators=[方法,] ---->单独给这个字段加校验规则
name=serializers.CharField(validators=[方法,])
3.局部钩子校验规则
4.全局钩子校验规则
新增图书接口
前端页面传入的数据格式
{"name":"世界顶级思维","price":1000,"publish":1,"authors":[1,2]}
书写视图类中的post方法
class BookAPIView(APIView):
def get(self,request):
books = Book.objects.all()
print(books)
ser = BookSerializer(instance=books,many=True)
return Response(ser.data)
# 新增
def post(self,request):
ser = BookSerializer(data=request.data)
if ser.is_valid():
ser.save() # 书写序列化类中create方法
return Response({'code':100,'msg':'新增成功'})
else:
return Response({'code': 101, 'msg': ser.errors})
序列化类中,使用read_only
和write_only
分别定制序列化和反序列化的序列外键字段,并书写新增图书的create
方法,read_only
和write_only
的目的是:需要序列化和反序列化的类,就可以使用这两种方法
from .models import Book
class BookSerializer(serializers.Serializer):
# name和price,既可以序列化,又可以反序列化,即写又读,不用加read_only和write_only
name = serializers.CharField(max_length=8)
price = serializers.CharField()
# 只用来做序列化 只读 read_only
publish_detail = serializers.DictField(read_only=True)
author_list = serializers.ListField(read_only=True)
# 只用来反序列化 只写 write_only
publish= serializers.CharField(write_only=True)
authors= serializers.ListField(write_only=True)
# 新增要重写create方法
def create(self, validated_data):
# validated_data 是校验过的数据
# 新增一本图书
book = Book.objects.create(name=validated_data.get('name'),price=validated_data.get('price'),publish_id=validated_data.get('publish'))
# 作者也要关联上
# book.authors add remove set clear...
book.authors.add(*validated_data.get('authors'))
# book.authors.add(1,2)
return book
修改图书接口
修改图书的接口跟新增图书不同,需要传主键值>>书写新的接口
path('books/<int:pk>/',views.BookDetailAPIView.as_view())
视图类:先查询(查询单条get方法)-----再修改(修改put方法)
class BookDetailAPIView(APIView):
# 查询单条
def get(self, request,pk):
book = Book.objects.filter(pk=pk).first()
print(book)
ser = BookSerializer(instance=book)
return Response(ser.data)
def put(self, request, pk):
book = Book.objects.filter(pk=pk).first()
ser = BookSerializer(data=request.data, instance=book)
if ser.is_valid():
ser.save()
return Response({'code': 100, 'msg': '修改成功'})
else:
return Response({'code': 101, 'msg': ser.errors})
反序列化类
from .models import Book
class BookSerializer(serializers.Serializer):
# name和price,既可以序列化,又可以反序列化,即写又读,不用加read_only和write_only
name = serializers.CharField(max_length=8)
price = serializers.CharField()
# 只用来做序列化 只读 read_only
publish_detail = serializers.DictField(read_only=True)
author_list = serializers.ListField(read_only=True)
# 只用来反序列化 只写 write_only
publish = serializers.CharField(write_only=True)
authors = serializers.ListField(write_only=True)
# 新增要重写create方法
def create(self, validated_data):
pass
def update(self, instance, validated_data):
# validated_data 校验过后的数据,{name:红楼梦,price:19,publish:1,authors:[1,2]}
instance.name = validated_data.get('name')
instance.price = validated_data.get('price')
instance.publish_id = validated_data.get('publish')
# 先清空多表关系的数据,再用add,否则会在后面追加,造成数据条重复
authors = validated_data.get('authors')
instance.authors.clear()
instance.authors.add(*authors)
instance.save() # 保存
return instance
反序列化字段校验其他
-
校验自有的字段,比如book中的name
name = serializers.CharField(max_length=8,error_messages={'max_length:不能超过8位'})
-
validators=[方法,] 忽略
-
局部钩子
# 局部钩子 def validate_name(self,name): if name.starswith('c'): raise ValidationError('不能以h开头') else: return name
!
-
全局钩子
# 全局钩子 def validate(self,attr): if attr.get('name')==attr.get('publish'): raise ValidationError('出版社名和书名不能一致') else: return attr
问题:反序列化的字段一定跟表模型的字段是对应的吗?
不一定
反序列化类校验部分源码解析(了解)
- 反序列化校验的开始
视图函数中ser.is_valid()
被执行时,就会进行反序列化校验,校验通过返回True
,否则返回False
-
反序列化的过程
- 1
ser.is_valid()`` 是序列化类的对象,假设序列化类是
BookSerializer,当执行
is_valid时,首先在序列化类产生的对象中查找,当找不到>>会找到父类
BaseSerializer`中有 :【raise_exception:先注意】
def is_valid(self, *, raise_exception=False): if not hasattr(self, '_validated_data'): try: # self序列化类的对象,属性中没有_validated_data,一定会走这句【核心】 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._validated_data = self.run_validation(self.initial_data)"""
-
2 不能直接按住ctrl键点击
执行顺序是:从
BookSerialier
-->Serializer
-->BaseSerializer
-->Field
当BaseSerializer
找到了is_valid
-->执行self.run_validation(self.initial_data)
,我们直接点击是父类的run即(Field
中的run_validation
方法),而最终从下往上找,从Serializer
类中找了run_validation
(最终执行的方法)
- 1
```python
def run_validation(self, data=empty):
# 字段自己的,validates方法
(is_empty_value, data) = self.validate_empty_values(data)
if is_empty_value:
return data
# 局部钩子----【局部钩子】
value = self.to_internal_value(data)
try:
self.run_validators(value)
# 全局钩子--》如果在BookSerializer中写了validate,优先走它
value = self.validate(value)
except (ValidationError, DjangoValidationError) as exc:
raise ValidationError(detail=as_serializer_error(exc))
return value
```
- 3布局钩子的校验
```python
# 局部钩子 self.to_internal_value(data) -->> self是BookSerializer的对象,从根上查找
def to_internal_value(self, data):
ret = OrderedDict()
errors = OrderedDict()
fields = self._writable_fields
# fields写在序列化类中一个个字段类的对象
for field in fields:
# self BookSerializer的对象,反射validate_name
validate_method = getattr(self, 'validate_' + field.field_name, None)
try:
# 在执行BookSerializer类中的validate_name方法,传入了要校验的数据
validated_value = validate_method(validated_value)
except ValidationError as exc:
errors[field.field_name] = exc.detail
else:
set_value(ret, field.source_attrs, validated_value)
if errors:
raise ValidationError(errors)
return ret
```
ModelSerializer使用
ModelSerializer
继承了Serializer
,帮我们完成了很多的操作,比如跟表模型强关联,大部分的请求post
和put
等,都不需要 在序列化里面书写create
和update
方法了。
fields='__all__'
序列化所有Book中的字段
fields= ['name','price']
序列化所有Book中name和price字段名
fields = ['name', 'price', 'publish_detail', 'author_list', 'publish', 'authors']
序列化所有Book中的name和price字段字段
如果meta里面的fields等于上面时,extra_kwargs只定制了name字段,查询出来的结果('name', 'price', 'publish_detail', 'author_list', 'publish', 'authors')如下
- 如果fields和extra_keargs都定制了如下图,查询所有的图书得到的结果('name', 'price', 'publish_detail', 'author_list')如下二图
总结
ModelSerializer 继承自Serializer,我们可以直接可以查询多条,单条,新增图书和修改图书等操作
针对fields=['name','photo','gender','addr']
里面的参数要求:
- 只要是序列化的字段和反序列化的字段,都要在这注册 如:publish_detail,publish,authors
- 序列化的字段,可能不是表模型的字段,是自己写的方法
- 序列化的字段,可能是使用SerializerMethodField,也要注册
from .models import Book
from rest_framework.exceptions import ValidationError
class BookSerializer(serializers.ModelSerializer):
# 跟表有关联
class Meta:
model = Book # 跟book表建立了关系 序列化表和表模型类
# fields = '__all__' # 序列化所有的Book中的字段,id name price,publish,authors
# fields = ['name', 'price'] # 序列化所有Book中name和price字段名
fields = ['name', 'price', 'publish_detail', 'author_list', 'publish', 'authors'] # 序列化所有Book中name和price字段名
# 定制name 反序列化,最长不能超过8位,给字段类加属性----方法一
extra_kwargs = {
'name': {'max_length': 8},
'publish_detail': {'read_only': True},
'authors_list': {'read_only': True},
'publish': {'write_only': True},
'authors': {'write_only': True},
}
# 如果Meta写了__all__,就相当于复制了表模型中的字段,放在这里,做了映射
# name = serializers.CharField(max_length=32)
# price = serializers.CharField(max_length=32)
# 定制name反序列化时,最长不能超过8,给字段类加属性--方式二 重写name字段
# name = serializers.CharField(max_length=8) # 如果重写name字段,校验的规则是>>>优先校验重写的name
# 同理,所有的read_only和write_only 都可以通过重写或使用extra_kwargs传入
# 终版,把这个序列化类跟之前一模一样的项目
publish_detail = serializers.SerializerMethodField(read_only=True)
def get_publish_detail(self, obj):
return {'name': obj.publish.name, 'addr': obj.publish.addr}
author_list = serializers.SerializerMethodField(read_only=True)
def get_author_list(self, obj):
list = []
for author in obj.authors.all():
list.append({'name': author.name, 'phone': author.phone})
return list
# 局部钩子和全局钩子跟之前完全一样
def validate_name(self, name):
if name.startswith('sb'):
raise ValidationError('不能sb')
else:
return name
练习题
- 基于序列化器编写5个接口 Publish
- 原生的request没有data属性,如何实现一个原生的request.data,无论什么编码格式提交的数据提交都有值,
FBV--->写个装饰器
# 通过装饰器做,装饰器视图函数的,以后都会有request
def outer(func):
def inner(request, *args, **kwargs):
"""
request:新生的request
urlencoded/form-data:request.POST就有值
如果request.POST没有值说明前端传入的编码格式是json格式
"""
try:
print(request.body)
request.data = json.loads(request.body) # 表示是json格式遍布
except Exception as e:
request.data = request.POST
res = func(request, *args, **kwargs)
return res
return inner
@outer # func= outer(index_func) 执行index_func()>>>本质就是outer(index_func)(request) >>> 本质就是inner(request)
def index_func(request):
print(request.POST) # urlencoded,form-data----->QueryDict
print(request.data) # 报错
return HttpResponse('OK')
注意:根据前端传入的编码格式不一样,在打印request.data
的操作之前,就做装饰器校验前端传入的数据格式做异常处理,如果传入的是json
格式,将request.body
拿到的数据>>>反序列化后并赋值给request.data
;如果传入的是urlencoded/form-data
,我们通过request.POST
就可以拿到值(字典)
- 继承Serializer的方式编写五个接口
路由
urlpatterns = [
path('admin/', admin.site.urls),
path('books/',views.BookAPIView.as_view()),
path('books/<int:pk>/',views.BookDetailView.as_view())
]
视图类
from rest_framework.views import APIView, Response
from .models import Book
from .serializer import BookSerializer
class BookAPIView(APIView):
# 获取序列化多条
def get(self, request):
books = Book.objects.all()
ser = BookSerializer(instance=books, many=True)
return Response(ser.data)
# 新增图书
def post(self, request):
ser = BookSerializer(data=request.data)
if ser.is_valid():
ser.save() # 书写create方法
return Response({'code': 100, 'msg': '新增成功'})
else:
return Response({'code': 100, 'msg': ser.errors})
class BookDetailView(APIView):
# 获取单条
def get(self, request, pk):
book = Book.objects.filter(pk=pk).first()
ser = BookSerializer(instance=book)
return Response(ser.data)
# 修改图书
def put(self, request, pk):
book = Book.objects.filter(pk=pk).first()
ser = BookSerializer(data=request.data, instance=book)
if ser.is_valid():
ser.save() # 书写update方法
return Response({'code': 100, 'msg': '修改成功'})
else:
return Response({'code': 100, 'msg': ser.errors})
# 删除
def delete(self, request, pk):
Book.objects.filter(pk=pk).delete()
return Response({'code': 100, 'msg': '删除成功'})
序列化类
from rest_framework import serializers
from rest_framework.serializers import SerializerMethodField
from .models import Book
class BookSerializer(serializers.Serializer):
name = serializers.CharField(max_length=8)
price = serializers.CharField(max_length=8)
# 定制在序列化类中
publish_detail = serializers.SerializerMethodField()
def get_publish_detail(self, obj):
return {'name': obj.publish.name, 'address': obj.publish.address}
author_list = serializers.SerializerMethodField()
def get_author_list(self, obj):
l1 = []
for author in obj.authors.all():
l1.append({'name': author.name, 'phone': author.phone,'gender':author.author_detail.gender})
return l1
# 反序列化
publish = serializers.CharField(write_only=True)
authors = serializers.ListField(write_only=True)
# 新增图书
def create(self, validated_data):
book=Book.objects.create(name=validated_data.get('name'), price=validated_data.get('price'),
publish_id=validated_data.get('publish'))
# 跟作者关联上
book.authors.add(*validated_data.get('authors'))
return book
# 修改图书
def update(self, instance, validated_data):
print(instance)
print(validated_data)
instance.name = validated_data.get('name')
instance.price = validated_data.get('price')
instance.publish_id = validated_data.get('publish')
# 修改作者与图书的关系,先清楚原来的绑定关系
instance.authors.clear()
instance.authors.add(*validated_data.get('authors'))
instance.save()
return instance
- 继承继承ModelSerialize编写五个接口
路由不变
urlpatterns = [
path('admin/', admin.site.urls),
path('books/',views.BookAPIView.as_view()),
path('books/<int:pk>/',views.BookDetailView.as_view())
]
视图类不变
from rest_framework.views import APIView, Response
from .models import Book
from .serializer import BookSerializer
class BookAPIView(APIView):
# 获取序列化多条
def get(self, request):
books = Book.objects.all()
ser = BookSerializer(instance=books, many=True)
return Response(ser.data)
# 新增图书
def post(self, request):
ser = BookSerializer(data=request.data)
if ser.is_valid():
ser.save() # 书写create方法
return Response({'code': 100, 'msg': '新增成功'})
else:
return Response({'code': 100, 'msg': ser.errors})
class BookDetailView(APIView):
# 获取单条
def get(self, request, pk):
book = Book.objects.filter(pk=pk).first()
ser = BookSerializer(instance=book)
return Response(ser.data)
# 修改图书
def put(self, request, pk):
book = Book.objects.filter(pk=pk).first()
ser = BookSerializer(data=request.data, instance=book)
if ser.is_valid():
ser.save() # 书写update方法
return Response({'code': 100, 'msg': '修改成功'})
else:
return Response({'code': 100, 'msg': ser.errors})
# 删除
def delete(self, request, pk):
Book.objects.filter(pk=pk).delete()
return Response({'code': 100, 'msg': '删除成功'})
序列化类简化
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book # 与book表关联
fields = ['name', 'price', 'get_publish_detail', 'get_author_list', 'publish', 'authors'] # 序列化字段
# 定制字段
extra_kwargs = {
'name': {'max_length': 8},
'publish_detail': {'read_only': True},
'author_list': {'read_only': True},
'publish': {'write_only': True},
'authors': {'write_only': True}
}
# 定制在模型表中,注意下面的字段get_publish_detail要和fields的里面的字段名要一致
def get_publish_detail(self):
return {'name': self.publish.name, 'address': self.publish.address}
def get_author_list(self):
l1 = []
for author in self.authors.all():
l1.append({'name': author.name, 'phone': author.phone,'gender':author.author_detail.gender})
return l1
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY