Django Rest Framework 소스 해결 - 시리얼화

14264 단어 Django
Django Rest Framework 소스 해결 - 시리얼화
예시 코드는 백엔드에서 작성한 코드와 서열화 과정만 보여 줍니다. 예시 코드는 다음과 같습니다.
파일을 나누는 것이 귀찮아서 모두views를 씁니다.py가 맞았다
import re

from django.db import models

from rest_framework import serializers
from rest_framework.viewsets import ModelViewSet


class UserProfile(models.Model):
    """
       
    """
    username = models.CharField(
        max_length=20, default="", verbose_name="  ", help_text="  ")
    email = models.EmailField(
        max_length=50, verbose_name="  ", help_text="  ")


class UserSerializer(serializers.ModelSerializer):
    """
         
    """
    class Meta:
        model = UserProfile
        fields = "__all__"


class UserViewSet(ModelViewSet):
    """
        :    
    """
    queryset = UserProfile.objects.all()
    serializer_class = UserSerializer

url.py
from django.contrib import admin
from django.urls import path, include

from study.views import UserViewSet
from rest_framework import routers

router = routers.SimpleRouter()
router.register(r"users", UserViewSet, base_name="users")

urlpatterns = [
    path('admin/', admin.site.urls),
    path(r"api/", include(router.urls))

1. 사용자 목록 보기 Serializer의 서열화 과정
1. 서열화된 대상 가져오기
class ListModelMixin:
    """
    List a queryset.
    """
    def list(self, request, *args, **kwargs):
        queryset = self.filter_queryset(self.get_queryset())

        page = self.paginate_queryset(queryset)
        if page is not None:
            serializer = self.get_serializer(page, many=True)
            return self.get_paginated_response(serializer.data)
        #        
        #            data        ,instance       
        serializer = self.get_serializer(queryset, many=True)
        return Response(serializer.data)
class GenericAPIView(views.APIView):
    .......
    serializer_class = None
    .......
    def get_serializer(self, *args, **kwargs):
        """
        Return the serializer instance that should be used for validating and
        deserializing input, and for serializing output.
        """
        #        
        serializer_class = self.get_serializer_class()
        kwargs['context'] = self.get_serializer_context()
        return serializer_class(*args, **kwargs)

    def get_serializer_class(self):
        """
        Return the class to use for the serializer.
        Defaults to using `self.serializer_class`.

        You may want to override this if you need to provide different
        serializations depending on the incoming request.

        (Eg. admins get full serialization, others get basic serialization)
        """
        assert self.serializer_class is not None, (
            "'%s' should either include a `serializer_class` attribute, "
            "or override the `get_serializer_class()` method."
            % self.__class__.__name__
        )
        #         Views   serializer_class = UserSerializer
        return self.serializer_class
    ......

이전 단계를 통해 우리는serializer=self를 알게 되었다.get_serializer(queryset, many=True)가 실행하는 UserSerializer 클래스의 실례화
2. Userializer 클래스의 실례화 과정
클래스 실례화 전에 new 방법을 실행합니다. 하나의 클래스의 생성 실례를 제어하는 과정에서 빈 대상을 생성하고, 하위 클래스가 없으면 부모 클래스의 new를 찾으며, new가 실행된 후에야 init 구조 방법을 실행할 수 있습니다.
User Serializer의 부류 Model Serializer에는 new 방법이 없고, Model Serializer의 부류 Serializer에도 new 방법이 없습니다. BaseSerlizer의 new 방법을 위로 찾으세요.
class BaseSerializer(Field):
    .......
    def __init__(self, instance=None, data=empty, **kwargs):
        self.instance = instance
        if data is not empty:
            self.initial_data = data
        self.partial = kwargs.pop('partial', False)
        self._context = kwargs.pop('context', {})
        kwargs.pop('many', None)
        super().__init__(**kwargs)

    def __new__(cls, *args, **kwargs):
        # We override this method in order to automagically create
        # `ListSerializer` classes instead when `many=True` is set.
        
        #       many=True,  cls.many_init(*args, **kwargs)
        if kwargs.pop('many', False):
            return cls.many_init(*args, **kwargs)
        return super().__new__(cls, *args, **kwargs)

    @classmethod
    def many_init(cls, *args, **kwargs):
        """
        This method implements the creation of a `ListSerializer` parent
        class when `many=True` is used. You can customize it if you need to
        control which keyword arguments are passed to the parent, and
        which are passed to the child.

        Note that we're over-cautious in passing most arguments to both parent
        and child classes in order to try to cover the general case. If you're
        overriding this method you'll probably want something much simpler, eg:

        @classmethod
        def many_init(cls, *args, **kwargs):
            kwargs['child'] = cls()
            return CustomListSerializer(*args, **kwargs)
        """
        allow_empty = kwargs.pop('allow_empty', None)
        child_serializer = cls(*args, **kwargs)
        list_kwargs = {
            'child': child_serializer,
        }
        if allow_empty is not None:
            list_kwargs['allow_empty'] = allow_empty
        list_kwargs.update({
            key: value for key, value in kwargs.items()
            if key in LIST_SERIALIZER_KWARGS
        })
        meta = getattr(cls, 'Meta', None)
        #   ListSerializer
        list_serializer_class = getattr(meta, 'list_serializer_class', ListSerializer)
        return list_serializer_class(*args, **list_kwargs)
      #           init    

3. Userializer 클래스의 인스턴스화 후 return Response(serializer.data) 실행
class ListSerializer(BaseSerializer):
    ......
    @property
    def data(self):
        #      data
        ret = super().data
        return ReturnList(ret, serializer=self)
      
class BaseSerializer(Field):
    ......
    @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.
' 'You should either call `.is_valid()` first, ' 'or access `.initial_data` instead.' ) # data is_valid raise AssertionError(msg) # _data if not hasattr(self, '_data'): # _errors if self.instance is not None and not getattr(self, '_errors', None): self._data = self.to_representation(self.instance) # is_valid to_representation elif hasattr(self, '_validated_data') and not getattr(self, '_errors', None): self._data = self.to_representation(self.validated_data) else: # get_initial self._data = self.get_initial() return self._data

4、우리가 전송한 instance 때문에self를 실행합니다.to_representation 함수에서 instance 실례가 전송되었습니다
class Serializer(BaseSerializer, metaclass=SerializerMetaclass):
    .......
    def to_representation(self, instance):
        """
        Object instance -> Dict of primitive datatypes.
        """
        ret = OrderedDict()
        #        
        fields = self._readable_fields

        for field in fields:
            try:
                #         field  
                attribute = field.get_attribute(instance)
            except SkipField:
                continue

            # We skip `to_representation` for `None` values so that fields do
            # not have to explicitly deal with that case.
            #
            # For related fields with `use_pk_only_optimization` we need to
            # resolve the pk value.
            check_for_none = attribute.pk if isinstance(attribute, PKOnlyObject) else attribute
            #     
            if check_for_none is None:
                #    
                ret[field.field_name] = None
            else:
                #     field to_representation   attribute
                ret[field.field_name] = field.to_representation(attribute)

        return ret
@property
def _readable_fields(self):
    #                 
    for field in self.fields.values():
        if not field.write_only:
            yield field
@cached_property
def fields(self):
    """
    A dictionary of {field_name: field_instance}.
    """
    # `fields` is evaluated lazily. We do this to ensure that we don't
    # have issues importing modules that use ModelSerializers as fields,
    # even if Django's app-loading stage has not yet run.
    fields = BindingDict(self)
    #   get_fields       
    for key, value in self.get_fields().items():
        #     
        fields[key] = value
    #      
    return fields
class ModelSerializer(Serializer):
    ......
    def get_fields(self):
        """
        Return the dict of field names -> field instances that should be
        used for `self.fields` when instantiating the serializer.
        """
        #   url_field_name
        if self.url_field_name is None:
            self.url_field_name = api_settings.URL_FIELD_NAME
        #     Meta  
        assert hasattr(self, 'Meta'), (
            'Class {serializer_class} missing "Meta" attribute'.format(
                serializer_class=self.__class__.__name__
            )
        )
        #    Meta   model  
        assert hasattr(self.Meta, 'model'), (
            'Class {serializer_class} missing "Meta.model" attribute'.format(
                serializer_class=self.__class__.__name__
            )
        )
        #           
        if model_meta.is_abstract_model(self.Meta.model):
            raise ValueError(
                'Cannot use ModelSerializer with Abstract Models.'
            )
        #        
        declared_fields = copy.deepcopy(self._declared_fields)
        #   model
        model = getattr(self.Meta, 'model')
        #       
        depth = getattr(self.Meta, 'depth', 0)
        #         0    10
        if depth is not None:
            assert depth >= 0, "'depth' may not be negative."
            assert depth <= 10, "'depth' may not be greater than 10."

        # Retrieve metadata about fields & relationships on the model class.
        #   model   
        info = model_meta.get_field_info(model)
        #   filed    
        field_names = self.get_field_names(declared_fields, info)

        # Determine any extra field arguments and hidden fields that
        # should be included
        #       
        extra_kwargs = self.get_extra_kwargs()
        extra_kwargs, hidden_fields = self.get_uniqueness_extra_kwargs(
            field_names, declared_fields, extra_kwargs
        )

        # Determine the fields that should be included on the serializer.
        fields = OrderedDict()
        #       
        for field_name in field_names:
            # If the field is explicitly declared on the class then use that.
            #           
            if field_name in declared_fields:
                #           
                fields[field_name] = declared_fields[field_name]
                continue
                	
            #          
            extra_field_kwargs = extra_kwargs.get(field_name, {})
            source = extra_field_kwargs.get('source', '*')
            if source == '*':
                source = field_name

            # Determine the serializer field class and keyword arguments.
            #                
            field_class, field_kwargs = self.build_field(
                source, info, model, depth
            )

            # Include any kwargs defined in `Meta.extra_kwargs`
            field_kwargs = self.include_extra_kwargs(
                field_kwargs, extra_field_kwargs
            )

            # Create the serializer field.
            #        field  
            fields[field_name] = field_class(**field_kwargs)

        # Add in any hidden fields.
        fields.update(hidden_fields)

        return fields

이 때 Model을 통해 서열화된 필드 값으로 변환됩니다. 속성을 가져오는 과정에서 filed입니다.get_attribute 방법은 사실 다음과 같은 방법을 사용했다.
def get_attribute(instance, attrs):
    """
    Similar to Python's built in `getattr(instance, attr)`,
    but takes a list of nested attributes, instead of a single attribute.

    Also accepts either attribute lookup on objects or dictionary lookups.
    """
    #       
    for attr in attrs:
        try:
            #      Mapping
            if isinstance(instance, Mapping):
                #       
                instance = instance[attr]
            else:
                #           
                instance = getattr(instance, attr)
        except ObjectDoesNotExist:
            return None
        if is_simple_callable(instance):
            try:
                instance = instance()
            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

좋은 웹페이지 즐겨찾기