(五) 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'

原文地址:https://www.cnblogs.com/donghaoblogs/p/12449635.html