zoukankan      html  css  js  c++  java
  • (五) rest_framework 序列化与解析器源码实现

    Serializer 类

    1. 指定序列化需要返回的 field,source为指定orm指定字段,可以通过. 获取外键关联对象。
    2. get_xxx_display 获取CharField choices 对应的值
    3. SerializerMethodField:自定义方法,执行实力化对象的 get_obj() 方法
    4. 每个对象触发field类的to_representation方法
    class AccountSerializers(serializers.Serializer):
        name = serializers.CharField(source='username')
        status = serializers.CharField(source='get_status_display') #拿到choice
        gender = serializers.CharField(source='get_gender_display')
        gp = serializers.CharField(source='group.group_name')
        user_roles = serializers.SerializerMethodField()
    
        gender_toast = serializers.SerializerMethodField()
        def get_gender_toast(self, obj):
            if obj.status == '1':
                return "英俊潇洒"
    
        def get_user_roles(self, obj):
            return obj.roles.values_list('name', flat=True)
    

    ModelSerializer 类

    内部实现fields的映射:serializer_field_mapping
    serializer_field_mapping = {
    ...
    models.CharField: CharField,
    models.CommaSeparatedIntegerField: CharField,
    models.DateField: DateField
    ....
    }
    直接指定返回字段获取自定义返回字段。

    class Myfield(serializers.BaseSerializer):
        """
        自定义field, 实现to_representation方法。
        """
        def to_representation(self, value):
            result = value.values_list('name', flat=True)
            return result
            
    class AccountSerializers(serializers.ModelSerializer):
        gp = serializers.CharField(source='group.group_name', allow_null=True)
        user_roles = serializers.SerializerMethodField()
        #自定义field
        my_roles = Myfield(read_only=True, source='roles.all') 
        #返回路由系统对应的链接。xxx为传递的参数名
        # re_path(r"^group/(?P<xxx>w+)", views.GroupsView.as_view(),
        # name='user_group')
        # 序列化时,需要传递 request参数,例如:
        # serializers = AccountSerializers(accounts,many=True, context={'request':request})
    	#   serilizer:user_group(app_name+view_name)
        group_link = serializers.HyperlinkedIdentityField(
            view_name='serilizer:user_group',
            lookup_field='group_id',
            lookup_url_kwarg='xxx',
        )
         
        class Meta:
            model = AccountModel
            # fields = '__all__'
            # 额外返回参数
            extra_kwargs = {'ggg': {'source': 'get_gender_display'}}
            # 需返回参数
            fields = ['username', 'gp', 'user_roles', 'ggg', 'my_roles', 'group_link']
            # 除了列表中,全部返回
            # exclude = ['id', 'roles', 'group']
            # 如果你想要显示多对多字段外键的所有字段 用depth=1
            # depth = 1 
    
        def get_user_roles(self, obj):
            return obj.roles.values_list('name', flat=True)
    
    #output:
    [
        {
            "username": "donghao",
            "gp": "第一组",
            "user_roles": [
                "学生",
                "it",
                "worker"
            ],
            "ggg": "男",
            "my_roles": [
                "学生",
                "it",
                "worker"
            ],
            "group_link": "http://127.0.0.1:8000/api/group/1"
        },
        {
            "username": "ttt",
            "gp": null,
            "user_roles": [],
            "ggg": "男",
            "my_roles": [],
            "group_link": "http://127.0.0.1:8000/api/group/None"
        }
    ]
    

    源码流程

    1. 进行序列化,实例化对象,执行 BaseSerializer 的 _new_ , _init_ 方法。
    2. 根据serializer类(是否指定many参数,默认False),分配给不容的serializer处理。
    def __new__(cls, *args, **kwargs):
            # `ListSerializer` classes instead when `many=True` is set.
            if kwargs.pop('many', False):
             	#交给 ListSerializer 类处理, 执行其 __init__方法
                return cls.many_init(*args, **kwargs)
            return super().__new__(cls, *args, **kwargs) #执行baseserializer __init__ 方法
    
    1. 执行serializer.data时,执行BaseSerializer类的data方法,再执行系列化对象的 to_representation 方法。

    当是返回单个instance时,执行serializer.to_representation() 方法
    当是返回列表时,执行listserializer.to_representation()方法, 遍历每一个对象,执行to_representation方法

    # self.child = kwargs.pop('child', copy.deepcopy(self.child))
    return [self.child.to_representation(item) for item in iterable]
    
    to_representation:
    for field in fields: #循环每个字段
        try:
            attribute = field.get_attribute(instance)
        except SkipField:
            continue
    
    get_attribute:
    def get_attribute(instance, attrs):
       # self.source_attrs = self.source.split('.') 通过 ' . '分割符号,连续去取相关的值,然后重新赋值
       for attr in attrs:# 循环取外键关联对象
           try:
               if isinstance(instance, Mapping):
                   instance = instance[attr]
               else:
                   instance = getattr(instance, attr)
           except ObjectDoesNotExist:
               return None
               # 是可以执行的一个函数。则直接调用。例如get_xxx_display等...
           if is_simple_callable(instance): #isinstance(object, types.FunctionType) 
               try:
                   instance = instance()
               except (AttributeError, KeyError) as exc:
                   raise ValueError('Exception raised in callable attribute "{}"; original exception was: {}'.format(attr, exc))
       return instance
    

    demo代码

    views:

    from rest_framework.views import APIView
    from rest_framework.response import Response
    from rest_framework.parsers import JSONParser
    from rest_framework.status import HTTP_200_OK, HTTP_404_NOT_FOUND
    from .serializers import AccountSerializers, GroupSerializers
    from .models import AccountModel, GroupModel
    
    class CreateAccount(APIView):
        parser_classes = [JSONParser, ]
    
        def post(self, request):
            AccountModel.objects.create(**request.data)
            return Response(data='ok', status=HTTP_200_OK)
    
    
    class AllAccounts(APIView):
        def get(self, request):
            accounts = AccountModel.objects.all()
            serializers = AccountSerializers(accounts, many=True, context={'request': request})
            return Response(data=serializers.data, status=HTTP_200_OK)
    
    
    class GroupsView(APIView):
        def get(self, request, *args, **kwargs):
            pk = kwargs.get("xxx", None)
            if not pk or pk == 'None':
                return Response(status=HTTP_404_NOT_FOUND)
            groups = GroupModel.objects.get(pk=pk)
            serializers = GroupSerializers(groups, many=False)
            return Response(data=serializers.data, status=HTTP_200_OK)
    

    models 模型:

    from django.db import models
    
    class AccountModel(models.Model):
        Gender = (
            ('0', '未知'),
            ('1', '男'),
            ('2', '女'),
        )
        STATUS = (
            ('0', '冻结'),
            ('1', '正常'),
            ('2', '删除'),
        )
        phone = models.CharField('手机号', max_length=11, blank=True, null=True)
        username = models.CharField('姓名', max_length=10, blank=True, null=True)
        wechat = models.CharField('微信号', max_length=150, blank=True, null=True)
    
        gender = models.CharField("性别", choices=Gender, default='0', max_length=1)
        status = models.CharField('状态', max_length=10, choices=STATUS, default='1')
        group = models.ForeignKey("GroupModel", on_delete=models.PROTECT, null=True)
        roles = models.ManyToManyField("RoleModel")
    
    class GroupModel(models.Model):
        group_name = models.CharField("组名", max_length=10)
    
    
    class RoleModel(models.Model):
        name = models.CharField("角色名", max_length=10)
    

    局部urls:

    from django.urls import path, re_path
    from . import views
    from rest_framework.urlpatterns import format_suffix_patterns
    
    app_name = "serilizer"  # serializers 打错
    
    urlpatterns = [
        path("account_create/", views.CreateAccount.as_view(), name='account_create'),
        path("accounts/", views.AllAccounts.as_view(), name='accounts'),
        re_path(r"^group/(?P<xxx>w+)", views.GroupsView.as_view(),
                name='user_group')
    ]
    
    Parser

    可以对请求的不同content_type 数据进行解析。
    请求进入视图,执行ApiView的initialize_request方法,将指定的parser封装到Request对象中。
    parsers=self.get_parsers() #[parser() for parser in self.parser_classes] 全局DEFAULT_PARSER_CLASSES
    当执行request.data时,执行rest_framwork的Request对象的data方法。

        @property
        def data(self):
            if not _hasattr(self, '_full_data'):
                self._load_data_and_files() # 根据content_type请求头找到对应的parser
            return self._full_data
    

    解析器选择:
    parser = self.negotiator.select_parser(self, self.parsers)

    常用Parser

    FormParser

    解析: media_type = 'application/x-www-form-urlencoded'

    JSONParser

    解析:
    media_type = 'application/json'

    MultiPartParser

    解析: media_type = 'multipart/form-data'

  • 相关阅读:
    Python爬虫_分布式爬虫
    Python爬虫_selenium
    Python爬虫_scrapy框架
    Python爬虫_高性能爬取
    Python爬虫_三种数据解析方式
    Python爬虫_requests模块
    Django
    Python爬虫相关基础概念
    MySQL 多表结构的创建与分析
    mysql
  • 原文地址:https://www.cnblogs.com/donghaoblogs/p/12449635.html
Copyright © 2011-2022 走看看