8.序列化
- 功能:
- 对请求数据进行验证
- 对Queryset进行序列化
8.1一个简单序列化:
import json
from api import models
from rest_framework import serializers
class RolesSerializer(serializers.Serializer):
title = serializers.CharField()
class RolesView(APIView):
def get(self,request,*args,**kwargs):
"""
#方式一:通过json序列化
roles = models.Role.objects.all().values("id",'title')
roles = list(roles)
ret = json.dumps(roles,ensure_ascii=False)
"""
roles = models.Role.objects.all()
# 实例化序列化器、many=True表示有多条数据
#如果roles为单个对象,序列化前需要设置many=False
ser = RolesSerializer(instance=roles,many=True)
print(ser.data)#[OrderedDict([('title', '医生')]), OrderedDict([('title', '学生')]), OrderedDict([('title', '老师')])]
ret = json.dumps(ser.data, ensure_ascii=False)
return HttpResponse(ret)
1&&序列化之Serializer
class UserInfoSerializer(serializers.Serializer):
num = serializers.IntegerField(source='user_type')
#通过source方法,choice字段可以通过get_字段名_display,显示所需结果
ming = serializers.CharField(source='get_user_type_display')#自动加括号成为可执行。
username = serializers.CharField()
password = serializers.CharField()
#外键,通过source方法,可以通过外键字段.关联表字段 显示所需结果
gp = serializers.CharField(source='group.title')
# 多对多关系需要使用SerializerMethodField,并自定义方法 get_字段。 返回所需结果。
rls = serializers.SerializerMethodField()
def get_rls(self,row):
role_obj_list = row.roles.all()
ret = []
for item in role_obj_list:
ret.append({"id":item.id,"title":item.title})
return ret
class UserInfosView(APIView):
def get(self, request, *args, **kwargs):
user = models.UserInfo.objects.all()
ser = UserInfoSerializer(instance=user,many=True)
ret = json.dumps(ser.data,ensure_ascii=False)
return HttpResponse(ret)
def post(self, request, *args, **kwargs):pass
2.&&ModelSerializer
#modelSerializer使用
class UserInfoSerializer2(serializers.ModelSerializer):
class Meta:
#表示关联UserInfo那张表
model = models.UserInfo
#表示表中所有字段
fields = "__all__"
class UserInfosView(APIView):
def get(self, request, *args, **kwargs):
user = models.UserInfo.objects.all()
ser = UserInfoSerializer2(instance=user,many=True)
ret = json.dumps(ser.data,ensure_ascii=False)
return HttpResponse(ret)
#对于显示数字的字段进行自定义:
class UserInfoSerializer2(serializers.ModelSerializer):
#序列化choice字段
num = serializers.IntegerField(source='user_type')
#序列化多对多关系的
rls = serializers.SerializerMethodField()
#序列化外键外键
gp = serializers.CharField(source='group.title')
def get_rls(self,row):
role_obj_list = row.roles.all()
ret = []
for item in role_obj_list:
ret.append({"id":item.id,"title":item.title})
return ret
class Meta:
model = models.UserInfo
fields = ["id","username","password","num","rls","gp"]
- 显示效果:
8.2序列化小总结:
*写序列化类可继承:
1.serializers.Serializer
2.serializers.ModelSerializer
*字段:
#序列化choice字段
num = serializers.IntegerField(source='user_type')
#序列化多对多关系的
rls = serializers.SerializerMethodField()
#序列化外键外键
gp = serializers.CharField(source='group.title')
8.3拓展:
#自定义字段
class MyField(serializers.CharField):
def to_representation(self, value):
#对字段value进行处理,返回处理后的new_value
new_value = value + "ABCDEFG"
return new_value
class UserInfoSerializer2(serializers.ModelSerializer):
...
#MyField为自己定义字段, source用于连接数据库中字段
myfiled = MyField(source="username")
class Meta:
model = models.UserInfo
fields = ["id","username","password","num","rls","gp","myfiled"]
class UserInfoSerializer3(serializers.ModelSerializer):
class Meta:
model = models.UserInfo
fields = "__all__"
depth = 1#官方文档建议0~10,自己建议3层左右
#depth使用要节制,层数设置越多,响应速率越慢
8.4hypermedialink 生成链接
- 将group生成url路由:
#1.路由,首先在路由定义group路由
url(r'^(?P[v1|v2]+)/group/(?P\d+)/$', views.GroupView.as_view(),name='gp'),
#2.定义完整视图类,以及用于序列化UserGroup的序列化类。
class GroupSerializer(serializers.ModelSerializer):
#UserGroup表中序列化器
class Meta:
model = models.UserGroup
fields = "__all__"
class GroupView(APIView):
def get(self,request,pk,*args,**kwargs):
#获取当前pk,拿当前pk值查询数据库获取对象
print(pk)
obj = models.UserGroup.objects.filter(pk=pk).first()
#序列化器序列化对象
ser = GroupSerializer(instance=obj,many=False)
#返回结果
ret = json.dumps(ser.data,ensure_ascii=False)
return HttpResponse(ret)
#3.更改用于UserInfosView视图类的序列化类
class UserInfoSerializer4(serializers.ModelSerializer):
#根据view_name根据所赋值,反向解析路由中的url
#group字段序列化完为当前group所得id对应的url
#lookup_url_kwarg 传入的url路由中的设置参数
#lookup_field 为当前表关联UserGroup表的字段
group = serializers.HyperlinkedIdentityField(view_name="gp",lookup_field="group_id",lookup_url_kwarg="pk")
class Meta:
model = models.UserInfo
fields = ["id","username","password","group"]
depth = 0
#4.UserInfosView使用序列化器
def get(self, request, *args, **kwargs):
ser = UserInfoSerializer4(instance=user,many=True,context={"request":request})
#当访问UserInfosView视图类对应路由
8.5序列化器源码解析
1.当视图类进行实例化序列化类做了如下操作:
#ModelSerializer 继承Serializer 继承BaseSerializer
#在BaseSerializer执行__new__方法,用于判断many是为True还是False:
def __new__(cls, *args, **kwargs):
if kwargs.pop('many', False):
#many = True, 对QuerySet进行处理
return cls.many_init(*args, **kwargs)
#many = False 对对象进行处理, 然后执行初始化方法__init__
return super().__new__(cls, *args, **kwargs)
#当many=True:为QuerySet对象,用ListSerializer进行处理
#当many=False:为单个对象,用Serializer进行处理
2.当实例化序列类调用data方法:
ser.data 执行:to_representation
def to_representation(self, instance):
ret = OrderedDict()
fields = self._readable_fields
for field in fields:
try:
#遍历循环对象, 该对象可以:对象.字段 eg:对象.username
attribute = field.get_attribute(instance)
except SkipField:
continue
3.执行get_attribute方法:
def get_attribute(self, instance):
try:
#instance为对象
#source_attrs:为一个列表 [group.title...] / get_user_type_display /roles.all
return get_attribute(instance, self.source_attrs)
4.执行get_attribute:
def get_attribute(instance, attrs):
#循环所有字段,将字段赋值给instance,
print("--->",attrs)#attrs为一个列表,遍历列表取字段。[id,]
print(instance)#istance 是UserInfo object对象
for attr in attrs:
try:
if isinstance(instance, Mapping):
instance = instance[attr]
else:
instance = getattr(instance, attr)
except ObjectDoesNotExist:
return None
#如果该对象是可执行的如: get_user_type_display 类型。那就加括号执行
if is_simple_callable(instance):
try:
instance = instance()
print("*****",instance)#1
except (AttributeError, KeyError) as exc:
# If we raised an Attribute or KeyError here it'd get treated
# as an omitted field in `Field.get_attribute()`. Instead we
# raise a ValueError to ensure the exception is not masked.
raise ValueError('Exception raised in callable attribute "{}"; original exception was: {}'.format(attr, exc))
return instance
"""
---> ['id']
UserInfo object
***** 1
---> ['username']
UserInfo object
***** aaa
---> ['password']
UserInfo object
***** 123
---> []
UserInfo object
---> ['id']
UserInfo object
***** 2
---> ['username']
UserInfo object
***** bbb
---> ['password']
UserInfo object
***** 123
---> []
UserInfo object
"""
8.6序列化器之数据校验
- 内置校验方法 和 自定义校验 和 钩子函数
class xxValidator(object):
"""自定义校验器"""
def __init__(self,base):
self.base = base
def __call__(self,value):
if not value.startswith(self.base):
msg = "标题必须以%s为开头"%self.base
raise serializers.ValidationError(msg)
class UserGroupSerializer(serializers.Serializer):
#定义title的error_messages错误信息显示,post为空时候会显示标题不能为空
#通过validators里面放置处理自定义校验器的方法。
title = serializers.CharField(error_messages={"required":"标题不能为空"},validators=[xxValidator("老男人"),])
#局部钩子,触发异常
def validate_title(self,value):
from rest_framework import exceptions
raise exceptions.ValidationError("就是想触发异常")
return value
class UserGroupView(APIView):
def post(self,request,*args,**kwargs):
print(request.data)
ser = UserGroupSerializer(data=request.data)
if ser.is_valid():
print(ser.validated_data)#为一个OrderedDict对象,打印提交数据
else:
print(ser.errors)#打印错误数据。如果提交空表单数据 会打印{'title': [ErrorDetail(string='标题不能为空', code='required')]}
return HttpResponse("提交数据")
8.7.序列化之源码深度解析
源码小技巧,当源码中有类实例化,一般将数据进行封装,封装成一个对象。一大堆赋值过程。有
__new__
方法会先执行此方法,用于构造对象。1.实例化一般是将数据进行封装到对象:
__new__
,__init__
- 当实例化对象many=True,会执行ListSerializer对象构造方法
- 当实例化对象many=False,会执行自身序列化器对象构造方法
2.调用对象data属性:
执行ListSerializer中to_representation方法。 对于数据展示,一直调用ListSerializer
class ListSerializer def to_representation(self, data): iterable = data.all() if isinstance(data, models.Manager) else data return [ #循环每一个数据库的对象,再根据每一个对象,去调用它的每个字段的to_representation来做显示 self.child.to_representation(item) for item in iterable ]
执行自己data,调用to_representation
#当执行自己Serializer class Serializer(BaseSerializer, metaclass=SerializerMetaclass): @property def data(self): #去父类执行data函数(父类BaseSerializer) ret = super().data #封装有序字典 return ReturnDict(ret, serializer=self) #-------------------------------------------------------------------------- class BaseSerializer: @property def data(self): if hasattr(self, 'initial_data') and not hasattr(self, '_validated_data'): msg = ( 'When a serializer is passed a `data` keyword argument you ' 'must call `.is_valid()` before attempting to access the ' 'serialized `.data` representation.\n' 'You should either call `.is_valid()` first, ' 'or access `.initial_data` instead.' ) raise AssertionError(msg) if not hasattr(self, '_data'): if self.instance is not None and not getattr(self, '_errors', None): self._data = self.to_representation(self.instance) elif hasattr(self, '_validated_data') and not getattr(self, '_errors', None): #执行to_representation,自己类中定义了此方法,去自己类中执行 self._data = self.to_representation(self.validated_data) else: self._data = self.get_initial() return self._data #-------------------------------------------------------------------------- class Serializer(BaseSerializer, metaclass=SerializerMetaclass): def to_representation(self, instance): ret = OrderedDict() fields = self._readable_fields #遍历循环每个field字段。field为我们序列化器写的每个字段。 #序列化器中定义每个字段帮助我们去数据库里面把数据库字段拿取过来,通过Charfield,Interfield等类进行展示 for field in fields: try: #去数据库中获取指定字段对应值 #比如: #当filed为id, 此时attribute=1 #当filed为pwd, 此时attribute=123 #如果设置特殊字段如:HyperlinkedIdentityField,它只会把当前字段对象取出来:obj attribute = field.get_attribute(instance) except SkipField: continue check_for_none = attribute.pk if isinstance(attribute, PKOnlyObject) else attribute if check_for_none is None: ret[field.field_name] = None else: #相当于: """ { id:1, CharField pwd:123, CharField group:obj, HyperlinkedIdentityField } """ #通过每个字段类型再执行to_representation, #因为有些字段一次无法拿到对应的值,所以,再通过各个字段的方法,如 #id:1 Charfield #会执行field.to_representation(attribute) === Charfield.to_representation(1) #Charfield中to_representation方法return six.text_type(value) === return str(value) #而HyperlinkedIdentityField 执行to_representation,用反射方法去数据库找lookup_filed设置的字段,去数据库拿值,然后根据咱们之前设置好的look_url_kwargs的值(此值为url路由上设置动态参数名字)。然后通过这2个值通过reverse反向生成url. ret[field.field_name] = field.to_representation(attribute) return ret
- 序列化器之校验源码
#is_valid() def is_valid(self, raise_exception=False): ... if not hasattr(self, '_validated_data'): try: #执行run_validation方法,此时注意要执行自己类的run_validation,为Serializer 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) #-------------------------------------------------------------------- def run_validation(self, data=empty): (is_empty_value, data) = self.validate_empty_values(data) if is_empty_value: return data #执行to_internal_value方法 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): if not isinstance(data, Mapping): message = self.error_messages['invalid'].format( datatype=type(data).__name__ ) raise ValidationError({ api_settings.NON_FIELD_ERRORS_KEY: [message] }, code='invalid') ret = OrderedDict() errors = OrderedDict() fields = self._writable_fields 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