rest framework 学习 序列化

序列化功能:对请求数据进行验证和对Queryset进行序列化

    Queryset进行序列化:

        1  序列化之Serializer

 1 class UserInfoSerializer(serializers.Serializer):
 2     num = serializers.IntegerField(source='user_type')
 3     #通过source方法,choice字段可以通过get_字段名_display,显示所需结果
 4     ming = serializers.CharField(source='get_user_type_display')#自动加括号成为可执行。
 5     username = serializers.CharField()
 6     password = serializers.CharField()
 7     #外键,通过source方法,可以通过外键字段.关联表字段  显示所需结果
 8     gp = serializers.CharField(source='group.title')
 9     # 多对多关系需要使用SerializerMethodField,并自定义方法 get_字段。 返回所需结果。
10     rls = serializers.SerializerMethodField()
11     def get_rls(self,row):
12         role_obj_list = row.roles.all()
13         ret = []
14         for item in role_obj_list:
15             ret.append({"id":item.id,"title":item.title})
16         return ret
17 
18 class UserInfosView(APIView):
19     def get(self, request, *args, **kwargs):
20         user = models.UserInfo.objects.all()
21         ser = UserInfoSerializer(instance=user,many=True)
22         ret = json.dumps(ser.data,ensure_ascii=False)
23         return HttpResponse(ret)
24     def post(self, request, *args, **kwargs):pass
View Code

  

2.ModelSerializer

 1 #对于显示数字的字段进行自定义:
 2 class UserInfoSerializer2(serializers.ModelSerializer):
 3     #序列化choice字段
 4     num = serializers.IntegerField(source='user_type')
 5     #序列化多对多关系的
 6     rls = serializers.SerializerMethodField()
 7     #序列化外键外键
 8     gp = serializers.CharField(source='group.title')
 9     def get_rls(self,row):
10         role_obj_list = row.roles.all()
11         ret = []
12         for item in role_obj_list:
13             ret.append({"id":item.id,"title":item.title})
14         return ret
15     class Meta:
16         model = models.UserInfo
17         fields = ["id","username","password","num","rls","gp"]
View Code

 3 自定义字段

 1 #自定义字段
 2 class MyField(serializers.CharField):
 3     def to_representation(self, value):
 4         #对字段value进行处理,返回处理后的new_value
 5         new_value = value + "ABCDEFG"
 6         return new_value
 7 class UserInfoSerializer2(serializers.ModelSerializer):
 8     ...
 9     #MyField为自己定义字段, source用于连接数据库中字段
10     myfiled = MyField(source="username")
11     class Meta:
12         model = models.UserInfo
13         fields = ["id","username","password","num","rls","gp","myfiled"]
View Code

4 depth使用

1 class UserInfoSerializer3(serializers.ModelSerializer):
2     class Meta:
3         model = models.UserInfo
4         fields = "__all__"
5         depth = 1#官方文档建议0~10,自己建议3层左右
6         #depth使用要节制,层数设置越多,响应速率越慢

5hypermedialink 生成链接

 1 #1.路由,首先在路由定义group路由
 2 url(r'^(?P<version>[v1|v2]+)/group/(?P<pk>\d+)/$', views.GroupView.as_view(),name='gp'),
 3 
 4 
 5 #2.定义完整视图类,以及用于序列化UserGroup的序列化类。
 6 class GroupSerializer(serializers.ModelSerializer):
 7     #UserGroup表中序列化器
 8     class Meta:
 9         model = models.UserGroup
10         fields = "__all__"
11 class GroupView(APIView):
12     def get(self,request,pk,*args,**kwargs):
13         #获取当前pk,拿当前pk值查询数据库获取对象
14         print(pk)
15         obj = models.UserGroup.objects.filter(pk=pk).first()
16         #序列化器序列化对象
17         ser = GroupSerializer(instance=obj,many=False)
18         #返回结果
19         ret = json.dumps(ser.data,ensure_ascii=False)
20         return HttpResponse(ret)
21 #3.更改用于UserInfosView视图类的序列化类
22 class UserInfoSerializer4(serializers.ModelSerializer):
23     #根据view_name根据所赋值,反向解析路由中的url
24     #group字段序列化完为当前group所得id对应的url 
25     #lookup_url_kwarg  传入的url路由中的设置参数
26     #lookup_field 为当前表关联UserGroup表的字段
27     group = serializers.HyperlinkedIdentityField(view_name="gp",lookup_field="group_id",lookup_url_kwarg="pk")
28     class Meta:
29         model = models.UserInfo
30         fields = ["id","username","password","group"]
31         depth = 0
32 #4.UserInfosView使用序列化器
33 def get(self, request, *args, **kwargs):
34     ser = UserInfoSerializer4(instance=user,many=True,context={"request":request})
35 #当访问UserInfosView视图类对应路由
View Code

 6序列化源码分析

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
View Code

对请求数据进行验证:

   序列化之数据效验

  内置效验方法和自定义效验和钩子函数

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("提交数据")
View Code

序列化之源码分析:

源码小技巧,当源码中有类实例化,一般将数据进行封装,封装成一个对象。一大堆赋值过程。有__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

 数据效验源码分析

 1 #is_valid()
 2     def is_valid(self, raise_exception=False):
 3           ...
 4         if not hasattr(self, '_validated_data'):
 5             try:
 6                 #执行run_validation方法,此时注意要执行自己类的run_validation,为Serializer
 7                 self._validated_data = self.run_validation(self.initial_data)
 8             except ValidationError as exc:
 9                 self._validated_data = {}
10                 self._errors = exc.detail
11             else:
12                 self._errors = {}
13 
14         if self._errors and raise_exception:
15             raise ValidationError(self.errors)
16         
17         return not bool(self._errors)
18 #--------------------------------------------------------------------
19     def run_validation(self, data=empty):
20         (is_empty_value, data) = self.validate_empty_values(data)
21         if is_empty_value:
22             return data
23         #执行to_internal_value方法
24         value = self.to_internal_value(data)
25         try:
26             self.run_validators(value)
27             value = self.validate(value)
28             assert value is not None, '.validate() should return the validated data'
29         except (ValidationError, DjangoValidationError) as exc:
30             raise ValidationError(detail=as_serializer_error(exc))
31 
32         return value
33 #----------------------------------------------------------------------
34     def to_internal_value(self, data):
35         if not isinstance(data, Mapping):
36             message = self.error_messages['invalid'].format(
37                 datatype=type(data).__name__
38             )
39             raise ValidationError({
40                 api_settings.NON_FIELD_ERRORS_KEY: [message]
41             }, code='invalid')
42 
43         ret = OrderedDict()
44         errors = OrderedDict()
45         fields = self._writable_fields
46 
47         for field in fields:
48             validate_method = getattr(self, 'validate_' + field.field_name, None)
49             primitive_value = field.get_value(data)
50             try:
51                 #执行字段本身内置方法。
52                 validated_value = field.run_validation(primitive_value)
53                 if validate_method is not None:
54                     #执行验证的钩子方法
55                     validated_value = validate_method(validated_value)
56             except ValidationError as exc:
57                 errors[field.field_name] = exc.detail
58             except DjangoValidationError as exc:
59                 errors[field.field_name] = get_error_detail(exc)
60             except SkipField:
61                 pass
62             else:
63                 set_value(ret, field.source_attrs, validated_value)
64 
65         if errors:
66             raise ValidationError(errors)
67 
68         return ret

 

posted @ 2020-11-17 22:58  dayu2020  阅读(133)  评论(0)    收藏  举报