1.序列化组件
前后端分离不能直接返回对象,json只能序列化列表和字典,所以我们在jango中使用序列化组件。
首先要导入利用我们安装好的djangorestframework(后面简称drf)中的一些模块。
from rest_framework import APIView # 我们重写的cbv继承APIview的一些方法和属性
from rest_framework.serializers import Serializer # 序列化的类,帮助我们使用序列化
from rest_framework.response import Response # 继承了HttpRespose,1.传字典使其序列化2.将数据返回给前端页面
django自带的序列化组件(不推荐使用):
from django.core import serializers # django自带的序列化组件
from rest_framework.views import APIView
class Books(APIView):
def get(self, request):
response = {'code':100, 'msg': '查询成功'}
books = models.Book.objects.all()
ret = serializers.serialize('json', books)
return HttpResponse(ret)
得到的结果:
难以控制json返回的信息。
使用drf的序列化组件:
1.新建一个序列化类继承Serializer
2.在类中要序列化的字段
在新建文件app01serializer中序列化要序列化的字段。
from app01 import models
from rest_framework import serializers
class BookSerializer(serializers.Serializer):
book_name = serializers.CharField()
price = serializers.CharField()
在视图中使用序列化的类:
1.实例化序列化的类产生的对象,在产生对象的时候,传入需要序列化的对象。
2.对象.data
3.return Response(对象.data)
在视图中:
class Books(APIView):
def get(self, request):
response = {'code': 100, 'msg': '查询成功'}
books = models.Book.objects.all()
# ret = serializers.serialize("json", books)
# 如果序列化多条,many=True(是queryset对象就要写)
# 如果序列化一条,many=False,也可以不写
bookser = BookSerializer(books, many=True)
return HttpResponse(bookser)
为何many=true为序列化多条源码分析:
如果many=true执行 cls.many_init(*args, **kwargs).
调用Respose:
from rest_framework.response import Response
class Books(APIView):
def get(self, request):
response = {'code': 100, 'msg': '查询成功'}
books = models.Book.objects.all()
# ret = serializers.serialize("json", books)
# 如果序列化多条,many=True(是queryset对象就要写)
# 如果序列化一条,many=False,也可以不写
bookser = BookSerializer(books, many=True)
return Response(bookser.data) # data并不是一个字符串,是一个对象,
结果:返回一个列表。
[
{
"book_name": "apple",
"price": "12.00"
},
{
"book_name": "banana",
"price": "3.00"
}
]
将数据放到字典中就可以得到一个简易版本的cbv:
class Books(APIView):
def get(self, request):
response = {'code': 100, 'msg': '查询成功'}
books = models.Book.objects.all()
# ret = serializers.serialize("json", books)
# 如果序列化多条,many=True(是queryset对象就要写)
# 如果序列化一条,many=False,也可以不写
bookser = BookSerializer(books, many=True)
print(type(bookser.data))
response['data'] = bookser.data
return Response(response)
.data为一个对象类型,将这个对象作为字典的值。
高级用法:
source:可以指定字段(name publish.name),可以指定方法
SerializerMethodField搭配方法使用:(get_字段名字)
publish_detail = serializers.SerializerMethodField(read_only=True) def get_authors(self, obj): ret = AuthorSerializer(instance=obj.authors.all(), many=True) return ret.data
read_only:反序列化时,不传
write_only:序列化时,不显示
利用source='name5',修改序列化模型表中的字段名(保证安全性,是字段名不暴露给外界)。
class BookSerializer(serializers.Serializer):
#指定source='name' ,表示序列化模型表中的name字段,重名命为name5(name和source='name'指定的name不能重名)
name5=serializers.CharField(source='name')
source不仅可以指定字段,还可以指定方法:
#如果要取 出版社的city source='publish.city'
publish=serializers.CharField(source='publish.name')
source不但可以指定一个字段,还可以指定一个方法
book_type = serializers.CharField(source='get_xx_display',read_only=True)
# 在模型层新增一个xx的字段
xx=models.IntegerField(choices=((0,'文学类'),(1,'情感类')),default=1,null=True)
# 这个地方隐藏了一个display的方法。
通过1对多获取数据,和通过多对多获取数据。
序列化出版社的详情,指定SerializerMethodField之后,可以对应一个方法,返回什么内容,publish_detail就是什么内容
publish_detail=serializers.SerializerMethodField(read_only=True)
#对应的方法固定写法get_字段名
def get_publish_detail(self,obj):
print(type(obj))
return {'name':obj.publish.name,'city':obj.publish.city}
# 通过类的的对象来拿取数据(对于宽展模型表的字段的修改不太友好)
authors = serializers.SerializerMethodField()
def get_authors(self, obj):
return [{'name': author.name, 'age': author.age} for author in obj.authors.all()]
取数据的第二种方式:
# 将外键所关联的表的字段序列化
class AuthorSerializer(serializers.Serializer):
name = serializers.CharField()
age = serializers.IntegerField()
# 不必通过多对多关系,直接查询表的数据
def get_authors(self, obj):
authorser = AuthorSerializer(obj.authors.all(), many=True)
return authorser.data
wiite_only=True:序列化的字段不显示(页面不显示该字段)
read_only=True 反序列化的时候,该字段不传
2.序列化的两种方式
Serializers:没有指定表模型
source:指定要序列化哪个字段,可以是字段,可以是方法
SerializerMethodField的用法
# 序列化出版社的详情,指定SerializerMethodField之后,可以对应一个方法,返回什么内容,publish_detail就是什么内容 publish_detail=serializers.SerializerMethodField(read_only=True) # 对应的方法固定写法get_字段名 def get_publish_detail(self,obj): return {'name':obj.publish.name,'city':obj.publish.city} #返回所有作者信息 authors=serializers.SerializerMethodField(read_only=True) def get_authors(self,obj): # return [ {'name':author.name,'age':author.age} for author in obj.authors.all()] authorser=AuthorSerializer(obj.authors.all(),many=True) return authorser.data
ModelSerializers:指定了表模型
class Meta:
model=表模型
#要显示的字段
fields='__all__'
fields=('id','name')或fields=['id', 'name']
#要排除的字段
exclude=('name')
#深度控制
depth=1重写某个字段:
在Meta外部,重写某些字段,方式同Serializers
class BookSerializer(serializers.ModelSerializer):
class Meta:
model=models.Book
# fields=('nid','name')
#不能跟fields同时使用
# exclude=['name',]
fields='__all__'
#深度是1,官方建议不要超过10,个人建议不要超过3
# depth=1
# xx=serializers.CharField(source='get_xx_display')
# authors=serializers.SerializerMethodField()
# def get_authors(self,obj):
# ret=AuthorSerializer(instance=obj.authors.all(),many=True)
# return ret.data
# name=serializers.CharField()
fields 表示显示那几个字段;exclude标识除了指定字段,其他都显示;depth表示跨表查显示,depth=1表示查一张相关联的表的信息(所有信息都会被显示出来),比较难以控制,而且depth查多张表,影响性能。
3.反序列化
Serializers:没有指定表模型
使用继承了Serializers序列化类的对象,反序列化
-在自己写的序列化类中重写create方法
-重写create方法,实现序列化
-在序列化类中:
def create(self, validated_data):
ret=models.Book.objects.create(**validated_data)
return ret
-在视图中:
def post(self,request):
bookser=BookSerializer(data=request.data)
if bookser.is_valid():
ret=bookser.create(bookser.validated_data)
return Response()
ps:继承了Serializers,就要重写create和update方法(方法名不能为save,必须为create或update)因为save会重写继承的save方法:
判断用create方法或update方法:
不能走save方法的原因:
序列化逻辑:
class BookSerializer(serializers.Serializer):
# source=book_name 修改模型表中的book_name字段名,修改的字段名和指定的字段名不能重复
name = serializers.CharField(source='book_name')
price = serializers.DecimalField(max_digits=8, decimal_places=2)
# book_type = serializers.CharField(source='get_classification_display', write_only=True) # 影响is_valid校验不过去,因为与模型表类型不同,且对应的值不一样
publish_detail = serializers.SerializerMethodField(read_only=True)
def get_publish_detail(self, obj):
return {'name':obj.publish.name, 'city':obj.publish.city}
# authors = serializers.SerializerMethodField(read_only=True)
def get_authors(self, obj):
# return [{'name': author.name, 'age': author.age} for author in obj.authors.all()]
authorser = AuthorSerializer(obj.authors.all(), many=True)
return authorser.data
def create(self, validated_data): # 函数名必须为create
ret=models.Book.objects.create(**validated_data)
return ret
视图层逻辑:
class Books(APIView):
def get(self, request):
response = {'code': 100, 'msg': '查询成功'}
books = models.Book.objects.all()
# ret = serializers.serialize("json", books)
# 如果序列化多条,many=True(是queryset对象就要写)
# 如果序列化一条,many=False,也可以不写
bookser = BookSerializer(books, many=True)
print(type(bookser.data))
response['data'] = bookser.data
return Response(response)
# 反序列化
def post(self, request):
# 实例化产生一个序列化的对象,data是要反序列化的字典
print(request.data)
bookser = BookSerializer(data=request.data)
# print(bookser.is_valid())
if bookser.is_valid(raise_exception=True):
bookser.save()
# 这里的save方法,本质上就是create方法,所以要在序列化的时候重写save方法
return Response()
ModelSerializers:指定了表模型
使用继承了ModelSerializers序列化类的对象,反序列化
-在视图中:
def post(self,request):
bookser=BookSerializer(data=request.data)
if bookser.is_valid():
ret=bookser.save()
return Response()
ModelSerializers在自己类中写了ceate和update方法,所以我们用的时候不必重写这两个方法。
Ps: 在从postman传传数据的时候,不能根据本表的外键关系,来添加数据,分析发现 is_valid(), 将这个表外键关系对应的表的信息自动过滤,解决这个问题,要我们自己在后端手动将这个id,添加到validdated_data.
4.反序列化的校验
-validate_字段名(self,value):
-如果校验失败,抛出ValidationError(抛出的异常信息需要去bookser.errors中取)
-如果校验通过直接return value
-validate(self,attrs)
-attrs所有校验通过的数据,是个字典
-如果校验失败,抛出ValidationError
-如果校验通过直接return attrs
一般在ModelSerializersz 内指定字段名(不建议使用__all__),使用全局钩子和局部钩子做校验。
#反序列化的校验(局部校验,全局校验)
def validate_name(self,value):
print(value)
raise exceptions.ValidationError('不能以sb开头')
# if value.startswith('sb'):
# raise ValidationError('不能以sb开头')
# return value
def validate(self,attrs):
print(attrs)
# if attrs.get('price')!=attrs.get('xx'):
# raise exceptions.ValidationError('name和price相等,不正常')
return attrs
因为我们用drf,遵循rest_ful规范,所以后端做校验的时候,在后端抛出异常信息。
#使用继承了ModelSerializers序列化类的对象,反序列化
def post(self,request):
#实例化产生一个序列化类的对象,data是要反序列化的字典
bookser=BookSerializer(data=request.data)
# bookser.data
if bookser.is_valid(raise_exception=True): # 给前端抛出异常
#清洗通过的数据
bookser.save()
else: # 后端抛出异常
print(bookser.errors['name'][0])
return Response()
钩子函数通过is_valid()这个入口函数进行如下源码校验:
校验字段:
5.源码阅读