drf框架用于前后端分离开发,分离开发的数据交互是通过json格式的数据来完成的。我们之前通过将字典序列化返回出去,其实drf框架本身就自带了序列化的组件。
一、django自带序列化组件
django也有自带的序列化组件,但是它默认是携带很多不需要的数据,不能控制其携带的数据,因此只做了解即可。
from django.core import serializers
def test(request):
book_list = models.Book.objects.all()
ret = serializers.serialize("json", book_list)
return HttpResponse(ret)
返回的结果(其中二进制的数据是汉字转码):
[{"model": "app01.book", "pk": 1, "fields": {"name": "\u5251\u6765", "publish": "\u5317\u4eac\u51fa\u7248\u793e"}}, {"model": "app01.book", "pk": 2, "fields": {"name": "\u96ea\u4e2d\u608d\u5200\u884c", "publish": "\u4e0a\u6d77\u51fa\u7248\u793e"}}]
二、 序列化组件
使用drf的序列化组件:
- 首先要新建一个序列化类继承Serializer
- 在类中写要序列化的字段
在视图中使用序列化的类:
- 实例化序列化的类产生对象,在产生对象的时候,传入需要序列化的对象(queryset)
- return Response(对象.data)
高级用法:
- source:可以指定字段(name publish.name),可以指定方法,
- SerializerMethodField搭配方法使用(get_字段名字)
publish_detail = serializers.SerializerMethodField(read_only=True) def get_publish_detail(self, obj): return {'name':obj.publish.name,'city':obj.publish.city}
- read_only:反序列化时,不传
- write_only:序列化时,不显示
三、序列化的两种方式
models.py
from django.db import models
class Book(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=32)
price = models.DecimalField(max_digits=5, decimal_places=2)
publish_date = models.DateField(null=True)
types = models.IntegerField(choices=((0, '文学类'), (1, '情感类')), default=1, null=True)
publish = models.ForeignKey(to='Publish', to_field='id', on_delete=models.CASCADE, null=True)
authors = models.ManyToManyField(to='Author')
def __str__(self):
return self.name
def test(self):
return 'xxx'
class Author(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=32)
age = models.IntegerField()
class Publish(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=32)
city = models.CharField(max_length=32)
email = models.EmailField()
def __str__(self):
return self.name
3.1、Serializers:没有指定表模型
创建完表之后,首先要对其中的字段进行序列化,我们新建了一个类去继承serializers.Serializer的类。
反序列化通过create方法完成
from app01 import models
from rest_framework import serializers
class AuthorSerializer(serializers.Serializer):
name = serializers.CharField()
age = serializers.CharField()
class BookSerializer(serializers.Serializer):
"""为了不把表内的名字暴露,可以返回别名,然后用source指定模型表中的字段名,但是别名不能与表中的字段名重名"""
names = serializers.CharField(source='name')
'''
write_only:序列化的时候,该字段不显示
read_only:反序列化的时候,该字段不传
'''
price = serializers.CharField(write_only=True)
'''出版社的详情在另一张表,如果要取出版社的city,需要使用source="publish.city" '''
publish = serializers.CharField(source='publish.name',read_only=True)
'''source不但可以指定一个字段,还可以指定一个方法'''
book_type = serializers.CharField(source='get_types_display', read_only=True)
'''序列化出版社的详情:指定SerializerMethodField之后,可以对应一个方法,
返回什么内容,publish_detail就是什么内容'''
publish_detail = serializers.SerializerMethodField(read_only=True)
# 对应的方法名字格式固定:get_字段名
def get_publish_detail(self, obj):
print(type(obj)) # <class 'app01.models.Book'>
return {'name': obj.publish.name, 'city': obj.publish.city}
# 返回所有作者信息
authors = serializers.SerializerMethodField(read_only=True)
def get_authors(self, obj): # obj就是要序列化的对象
'''可以直接返回一个列表推导式:
return [ {'name':author.name,'age':author.age} for author in obj.authors.all()]
也可以写一个Author的序列化类,调用它来进行序列化
'''
authorser = AuthorSerializer(obj.authors.all(), many=True)
return authorser.data
# 反序列化的校验:局部校验:validate_字段名(如果起别名就是别名)
def validate_names(self, value): # value就是前端传过来的值
if value.startswith('sb'):
raise ValidationError('不能以sb开头')
return value
# 反序列化,重写了create方法
def create(self, validated_data):
res = models.Book.objects.create(**validated_data)
return res
然后我们要在视图函数中写CBV,把我们查处来的Book的queryset对象传到序列化的类里面进行序列化,序列化完毕后,可以通过序列化的类实例化得到的对象.data的方法拿到结果,然后用Response返回json格式的数据,Response实际上也是继承了HttpResponse。
from rest_framework.views import APIView
from app01 import models
from rest_framework.response import Response
# 把对象转换成json格式字符串
from app01.app01_serializer import BookSerializer
class Books(APIView):
def get(self, request):
response = {'code': 100, 'msg': '查询成功'}
books = models.Book.objects.all()
'''
如果序列化多条,即queryset对象,就要写many=True;
如果只序列化一条,就可以不写;
instance是要序列化的对象
'''
bookser = BookSerializer(instance=books, many=True)
# print('view:',type(bookser.data))
# view: <class 'rest_framework.utils.serializer_helpers.ReturnList'>
response['data'] = bookser.data
return Response(response)
# 使用继承了Serializers序列化类的对象,反序列化
def post(self, request):
# 实例化产生一个序列化的对象,data是要反序列化的字典
bookser = BookSerializer(data=request.data)
if bookser.is_valid():
# 清洗通过的数据
res = bookser.create(bookser.validated_data)
print(bookser.errors)
return Response()
然后通过postman向后台发GET请求,可以拿到返回的数据:http://127.0.0.1:8000/books
{
"code": 100,
"msg": "查询成功",
"data": [
{
"names": "剑来",
"publish": "北京出版社",
"book_type": "情感类",
"publish_detail": {
"name": "北京出版社",
"city": "北京"
},
"authors": [
{
"name": "烽火戏诸侯",
"age": "30"
}
]
},
{
"names": "诛仙",
"publish": "上海出版社",
"book_type": "文学类",
"publish_detail": {
"name": "上海出版社",
"city": "上海"
},
"authors": [
{
"name": "萧鼎",
"age": "40"
}
]
}
]
}
然后再来看反序列化,我们之前已经写了create方法来完成反序列化的工作,下面先来写一下视图函数的post方法,利用post方法来完成反序列化的工作。post的视图函数已经在上面写了。我们通过postman给后端发一个post请求。
数据库中数据成功插入:
3.2、 ModelSerializers:指定了表模型
- ModelSerializers:指定了表模型
class Meta:
model=表模型
# 要显示的字段
fields=('__all__')
fields=('id','name')
# 要排除的字段
exclude=('name')
# 深度控制
depth=1
- 重写某个字段
在Meta外部,重写某些字段,方式同Serializers
序列化的文件:app01_serializer.py
from app01 import models
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
class AuthorSerializer(serializers.Serializer):
name = serializers.CharField()
age = serializers.CharField()
class BookSerializer(serializers.ModelSerializer):
class Meta:
# 指定要序列化的表模型
model = models.Book
# 指定要序列化的字段,'__all__'就是所有字段
# exclude = ('name',),表示除了name字段都序列化
# fields = ('id', 'name')
fields = '__all__'
# 跨表深度,最好不要超过3
depth = 1
# 可以在Meta外面对外键字段重写,拿到详细信息
authors = serializers.SerializerMethodField()
def get_authors(self, obj):
res = AuthorSerializer(instance=obj.authors.all(), many=True)
return res.data
# 反序列化的全局校验validate
def validate(self, attrs):
if attrs.get('price') <= 0:
raise ValidationError('价格不能为负数')
return attrs
视图函数:
from rest_framework.views import APIView
from app01 import models
from rest_framework.response import Response
# 把对象转换成json格式字符串
from app01.app01_serializer import BookSerializer
class Books(APIView):
def get(self, request):
response = {'code': 100, 'msg': '查询成功'}
books = models.Book.objects.all()
'''
如果序列化多条,即queryset对象,就要写many=True;
如果只序列化一条,就可以不写;
instance是要序列化的对象
'''
bookser = BookSerializer(instance=books, many=True)
# print('view:',type(bookser.data))
# view: <class 'rest_framework.utils.serializer_helpers.ReturnList'>
response['data'] = bookser.data
return Response(response)
# 使用继承了ModelSerializers序列化类的对象,反序列化
def post(self, request, *args, **kwargs):
# 实例化产生一个序列化类的对象,data是要反序列化的字典
bookser = BookSerializer(data=request.data)
if bookser.is_valid():
bookser.save()
else:
print(bookser.errors)
return Response()
3.3、反序列化
"""
-反序列化的校验
-validate_字段名(self,value):
-如果校验失败,抛出ValidationError(抛出的异常信息需要去bookser.errors中取)
-如果校验通过直接return value
-validate(self,attrs)
-attrs所有校验通过的数据,是个字典
-如果校验失败,抛出ValidationError
-如果校验通过直接return attrs
"""
反序列化已经在序列化的示例中加入了,通过post方法完成反序列化,继承serializers.Serializer的类需要在序列化的类中重写create方法。
继承serializers.ModelSerializer类的不用重写,在校验通过后,直接调用save方法即可。
四、源码分析
1、序列化组件,先调用__new__方法,如果many=True,生成ListSerializer对象,如果为False,生成Serializer对象
2、序列化对象.data方法--调用父类data方法---调用对象自己的to_representation(自定义的序列化类无此方法,去父类找);Aerializer类里有to_representation方法,for循环执行attribute = field.get_attribute(instance);再去Field类里去找get_attribute方法,self.source_attrs就是被切分的source;然后执行get_attribute方法,source_attrs。当参数传过去,判断是方法就加括号执行,是属性就把值取出来