04 序列化
什么是序列化
序列化是将对象转换为另一种数据格式的行为
为什么需要序列化?
想想django模型,它是一个python类。那么如何在浏览中将python类呈现为JSON呢?
使用django REST 序列化程序;同样将JSON转换为对象,称为反序列化。
可以这样理解:
- 通过将djagno模型转为JSON,在浏览中显示它们
- 使用 JSON payload向API发出CRUD请求
功能
- 对请求数据的验证
- 对QuerySet序列化
序列化器允许把像查询集QuerySet和模型实例这位的复杂数据转换为可以轻松渲染成JSON/XML或者其他内容类型的原生Python类开。序列化器还提供反序列化,在验证输入的数据之后允许解析数据转换回复杂类型。
REST framework中的serializers与Django的Form和ModelForm类非常像。我们提供了一个Serializer类,它为你提供了强大的通用方法来控制响应的输出,以及一个ModelSerializer类,它为创建用于处理模型实例和查询集的序列化程序提供了有用的快捷实现方式。
传统方式序列化JSON
class RolesView(APIView):def get(self,request,*args,**kwargs):roles=models.Role.objects.all().values('id','title')roles=list(roles)ret=json.dumps(roles, ensure_ascii=False) # 使用了json模块return HttpResponse(ret)
序列化器方式序列化JSON
from rest_framework import serializersclass RolesSerializer(serializers.Serializer):id = serializers.IntegerField()title = serializers.CharField()class RolesView(APIView):"""将 Roles 对象序列化 json"""def get(self,request,*args,**kwargs):# 多个对象# 对[obj1, obj2, obj3] 格式化,使用many参数roles = modles.Role.objects.all()ser = RolesSerializer(instance=roles, many=True)ret=json.dumps(ser.data, ensure_ascii=False)# 单个对象role = modles.Role.objects.all().first()ser = RolesSerializer(instance=role, many=False)ret=json.dumps(ser.data, ensure_ascii=False)return HttpResponse(ret)
自定义序列化字段
支持两种方式:
- 序列器定义时,字段使用参数
source或者使用SerializerMethodField字段 - 定义序列器时,继承自
serializers.ModelSerializer
from rest_framework import serializersclass UserInfoSerializer(serializers.Serializer):id = serializers.IntegerField()useranme = serializers.CharField(source="get_user_type_display") # row.get_user_type_displaypassword = serializers.CharField()gp = serializers.CharField(source="group.title")rls = serializers.SerializerMethodField() # 自定义显示det get_rls(self, now):role_obj_list = row.roles.all()ret = []for item in role_obj_list:ret.append({'id:item.id, 'title': item.title})return retclass UserInfoView(APIView):"""将 UserInfo 对象序列化 json"""def get(self,request,*args,**kwargs):# 多个对象# 对[obj1, obj2, obj3] 格式化,使用many参数roles = modles.UserInfo.objects.all()ser = UserInfoSerializer(instance=roles, many=True)ret=json.dumps(ser.data, ensure_ascii=False)return HttpResponse(ret)
自定义序列化类,基于继承
from rest_framework import serializersclass UserInfoSerializer(serializers.ModelSerializer):class Meta:model = models.UserInfo# fields = "__all__"fields = ['id', 'useranme', 'password', 'rls', 'group']# depth = 1 # depth = n
验证
验证器可以用于在不同类型的字段之间重新使用验证逻辑。 —django文档
大多数情况下,您在REST框架中处理验证时,只需依赖缺省字段验证,或者在序列化程序或字段类上编写显式验证方法。
但是,有时您会希望将验证逻辑放置到可重用组件中,以便在整个代码库中轻松地重用它。这可以通过使用验证器函数和验证器类来实现。
DRF验证
REST框架验证完全在序列化程序类上执行。
- 它引入了一个适当的问题分离,使用您的代码行为更加明显
- 使用快捷ModelSerializer类和使用显示Serializer类很容易切换。任何正在使用验证行为ModelSerializer都很容易复制。
- 打印repr一个序列化器实例将显示它到底应用了哪些验证规则。在在模型实例上没有额外的隐藏验证行为。
DRF常用验证器
UniqueValidator
用来强制 unique=True模型字段的约束。一个必需参数和一个可选的message参数
- queryset 必需,查询集对字段做唯一性检查
- message 验证失败时应该使用的错误消息
from rest_framework.validators import UniqueValidatorslug = SlugField(max_length=100,validators=[UniqueValidator(queryset=BlogPost.objects.all())])
UniqueTogeherValicator
用来强制unique_together对模型实例进行约束。有两个必需的参数和一个可选的message参数:
- queryset 必需,查询集对字段做唯一性检查
- fieldes 必需,应该创建唯一集体的字段的字段名称列表或者元组。必须作为序列化类中的字段已经存在。
- message 验证失败时错误提示消息
from rest_framework.validators import UniqueTogetherValidatorclass ExampleSerializer(serializers.Serializer):# ...class Meta:# ToDo items belong to a parent list, and have an ordering defined# by the 'position' field. No two items in a given list may share# the same position.validators = [UniqueTogetherValidator(queryset=ToDoItem.objects.all(),fields=('list', 'position'))]
| 验证器 | 作用 |
|---|---|
| UniqueForDateValidator | |
| UniqueForMonthValidator | |
| UniqueForYearValidator | |
参数:
- queryset 必需,唯一性检查
- field 必需 在给定日期范围内唯一
- date_field required 确定唯一性约束的日期范围的字段名称
- message 错误提示消息
from rest_framework.validators import UniqueForYearValidatorclass ExampleSerializer(serializers.Serializer):# ...class Meta:# Blog posts should have a slug that is unique for the current year.validators = [UniqueForYearValidator(queryset=BlogPostItem.objects.all(),field='slug',date_field='published')]
日期字段可读写时设置方法
# 可写字段published = serializers.DateTimeField(required=True)# 只读字段,用户不可写published = serializers.DateTimeField(read_only=True, default=timezone.now)# 隐藏日期字段published = serializers.HiddenField(default=timezone.now)
为不同的请求方法使用相应的序列化类
DRF提供了一种非常方便的方式来开发RESTful应用程序。比如generics通用模块,它包含许多基于请求方法的有用的APIView。
但是,对于具有这些generics通用模块视图的序列化程序类,有以下麻烦。
# models.pyimport uuidfrom django.db import modelsclass Organization(models.Model):org_uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)name = models.CharField(max_length=40)class User(AbstractBaseUser):user_uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)# Omit username and all the rest of fields for simplicity.org = models.ForeignKey('Organization', on_delete=models.CASCADE,)
在定义的模型中常使用到的外键情况。当我们创建一个User或者需要更新User的org属性时,我们只想要向视图提供一个 org_uuid。但是当我们获取或者列出User用户实例的信息时,我们又更愿意看到更多信息而不仅仅是org_uuid。
因此,创建或者更新具有带有列表或者视图方法的不同序列化程序。我们通常为像 User这样的模型类定义以下至少两个序列化器类。
# serializers.pyfrom rest_framework import serializersfrom myapp import modelsclass OrganizationListViewSerializer(serializers.ModelSerializer):"""组织列视图序列器"""class Meta:model = models.Organizationfields = '__all__'class UserListViewSerializer(serializers.ModelSerializer):"""用户列视图序列器"""org = OrganizationListViewSerializer()class Meta:model = models.Userfields = '__all__'class UserCreateUpdateSerializer(serializers.ModelSerializer):"""用户创建视图序列器"""org = serializers.PrimaryKeyRelatedField(queryset=models.Organization.objects.all())class Meta:model = models.Userfields = '__all__
如上,它可以列出或者查看用户信息或者使用嵌套的序列化程序来扩展用户的组织信息。另一方面,org_uuid足以创建或者更新User实例
回到generics通用模块。基于REST的原则,也许是 generics.ListCreateAPIView和generics.RetrieveUpdateAPIView 是最常用的观点,这是基于请求方法设计。DRF在view中做得很好,但是在Serializer中它没有更进一步。
# views.pyfrom rest_framework import exceptionsfrom rest_framework import genericsfrom myapp import modelsfrom myapp import serializers as serclass MethodSerializerView(object):'''通过请求方法获得不同的通用类For example:method_serializer_classes = {('GET', ): MyModelListViewSerializer,('PUT', 'PATCH'): MyModelCreateUpdateSerializer}'''method_serializer_classes = Nonedef get_serializer_class(self):assert self.method_serializer_classes is not None, ('Expected view %s should contain method_serializer_classes ''to get right serializer class.' %(self.__class__.__name__, ))for methods, serializer_cls in self.method_serializer_classes.items():if self.request.method in methods:return serializer_clsraise exceptions.MethodNotAllowed(self.request.method)class UsersListCreateView(MethodSerializerView, generics.ListCreateAPIView):'''API: /usersMethod: GET/POST'''queryset = models.User.objects.all()method_serializer_classes = {('GET', ): ser.UserListViewSerializer,('POST'): ser.UserCreateUpdateSerializer}class UsersDetailView(MethodSerializerView, generics.RetrieveUpdateAPIView):'''API: /user/:user_uuidMethod: GET/PUT/PATCH'''queryset = models.User.objects.all()method_serializer_classes = {('GET', ): ser.UserListViewSerializer,('PUT', 'PATCH'): ser.UserCreateUpdateSerializer}
注意:MethodSerializerView应放在
generics通用模块视图之前,因此它可以覆盖get_serializer_class方法。
最后,在处理generics通用模块视图时,我们有多个串行器选择,所有这些都基于请求方法😀。
