[Django] Django Rest Framework Serializer & generic API View

Django Rest Framework Serializer & generic API View

API View

Post - create flow

def post(self, request, *args, **kwargs): 
    return self.create(request, *args, **kwargs)
def create(self, request, *args, **kwargs):   
    serializer = self.get_serializer(data=request.data)
    serializer.is_valid(raise_exception=True)

get_serializer는 어떻게 동작하는가.

def get_serializer(self, request, *args, **kwargs):
    serializer_class = get_serializer_class()
    kwargs['context'] = self.get_serializer_context()
    return serializer_class(*args, **kwargs) 

def get_serializer_context(self):
    """
    Extra context provided to the serializer class.
    """
    return {
        'request': self.request,
        'format': self.format_kwarg,
        'view': self
    }

def get_serializer_class(self):
    assert self.serializer_class is not None()
        return self.serializer_class

### BaseSerializer : __init__ 메소드 

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(BaseSerializer, self).__init__(**kwargs)
  1. get_serializer(data=request.data) : 
    • get_serializer_class 메소드를 호출, data=request.data는 kwargs로 포함됨.
    • kwargs = {“data”:request.data}에 get_serializer_context() 메소드를 호출.
    • kwargs = {“data”: request.data, “context”: {‘request’: self.request, ‘format’: self.format_kwargs, ‘view’: self} 
      마지막으로 해당 serializer_class(*args, **kwargs)를 리턴해준다.
  2. get_serializer_class(self) : 
    • APIView를 상속받은 Custom Serializer에서 정의한 serializer_class를 가져옴. 정의하지 않았을 경우에 오류 띄움
  3. serializer_class(*args, **kwargs): 
    • kwargs에는 ‘data’와 ‘context’ 가 들어가 있다.
  4. BaseSerializer의 init 메소드로 올라가 보면 
    • data=request.data가 들어가게 되어 self.initial_data = data가 저장된다. 또한 partial은 언제 쓰이는지 아직 잘 모르겠지만 일단 넘어가겠다.
    • partial과 context는 kwargs에서 pop되어 제거되고 kwargs는 빈 딕셔너리 형태로 init메소드는 끝난다.

Serializer의 is_valid 는 어떻게 동작하는가

def is_valid(self, raise_exception=False):
    assert not hasattr(self, 'restore_object'), (
        'Serializer `%s.%s` has old-style version 2 `.restore_object()` '
        'that is no longer compatible with REST framework 3. '
        'Use the new-style `.create()` and `.update()` methods instead.' %
        (self.__class__.__module__, self.__class__.__name__)
    )

    assert hasattr(self, 'initial_data'), (
        'Cannot call `.is_valid()` as no `data=` keyword argument was '
        'passed when instantiating the serializer instance.'
    )

    if not hasattr(self, '_validated_data'):
        try:
            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):
    """
    Validate a simple representation and return the internal value.

    The provided data may be `empty` if no representation was included
    in the input.

    May raise `SkipField` if the field should not be included in the
    validated data.
    """
    (is_empty_value, data) = self.validate_empty_values(data)
    if is_empty_value:
        return data
    value = self.to_internal_value(data)
    self.run_validators(value)
    return value

def validate_empty_values(self, data):
    """
    Validate empty values, and either:

    * Raise `ValidationError`, indicating invalid data.
    * Raise `SkipField`, indicating that the field should be ignored.
    * Return (True, data), indicating an empty value that should be
      returned without any further validation being applied.
    * Return (False, data), indicating a non-empty value, that should
      have validation applied as normal.
    """
    if self.read_only:
        return (True, self.get_default())

    if data is empty:
        if getattr(self.root, 'partial', False):
            raise SkipField()
        if self.required:
            self.fail('required')
        return (True, self.get_default())

    if data is None:
        if not self.allow_null:
            self.fail('null')
        return (True, None)

    return (False, data)


def to_internal_value(self, data):
    """
    Transform the *incoming* primitive data into a native value.
    """
    raise NotImplementedError(
        '{cls}.to_internal_value() must be implemented.'.format(
            cls=self.__class__.__name__
        )
    )

def run_validators(self, value):
    """
    Test the given value against all the validators on the field,
    and either raise a `ValidationError` or simply return.
    """
    errors = []
    for validator in self.validators:
        if hasattr(validator, 'set_context'):
            validator.set_context(self)

        try:
            validator(value)
        except ValidationError as exc:
            # If the validation error contains a mapping of fields to
            # errors then simply raise it immediately rather than
            # attempting to accumulate a list of errors.
            if isinstance(exc.detail, dict):
                raise
            errors.extend(exc.detail)
        except DjangoValidationError as exc:
            errors.extend(get_error_detail(exc))
    if errors:
        raise ValidationError(errors)
  1. is_valid(self, raise_exception=False):
    • self객체를 받아 _validated_date 속성을 넣어준다. 따라서 이후에 validated_date 를 사용하기 위해서는 is_valid 메소드를 우선 통과해야할것이다.
    • self 객체에 restore_object 속성이 없다고 선언한다. 왜 하는지 아직 모르겠다.
    • self 객체에 initial_data 속성이 있다고 선언한다. initial_data는 위에서 입력한 request.data가 들어갈 것이다.
    • self 객체에 _validated_date 속성이 없는지 확인한다. 속성이 존재한다면 넘어가고, 존재하지 않는다면, self.run_validation(self.initial_data)를 호출해 validation을 진행한다. self._validated_data는 self.run_validation 메소드의 리턴값이 들어간다.
  2. run_validation(self, data=empty):
    • validation을 수행하는 메소드다.
    • validate_empty_values(data)의 도움을 받아 데이터가 빈값인지 확인한다.
    • 이 함수는 데이터가 비어있다면 validation을 진행하지 않고, return data
    • 이 함수는 데이터가 들어있다면, validation을 진행한다.
  3. validate_empty_values(data) :
    • 간단한 메소드다. 데이터가 비었는지 확인만 해준다.
  4. to_internal_value(data) :
    • 상당히 흥미로운 메소드다. 메소드 이름은 외부 데이터를 내부 데이터로 바꾼다는 의미같은데. Raise NotImplementedError() 코드 뿐이다. BaseSerializer에서 정의한 to_internal_value 메소드를 오버라이딩하지 않으면 에러를 띄운다는 것인데. 지금까지 사용하면서 오버라이딩한적이 없음에도 is_valid가 잘 작동했기에 아직 잘 이해되지 않는다.
    • 일단 data는 request.data가 빈 값이 아니라면 그대로 입력받아 request.data인데, 메소드 이름으
  5. run_validators(self, value) : 
    • 단순히 validation을 수행한다. 다만 따로 지정한 validator가 없을 경우에 기본 validator를 가져오고 그렇지 않을 경우,

Serializer Save Method

def save(self, **kwargs):
    assert not hasattr(self, 'save_object'), (
        'Serializer `%s.%s` has old-style version 2 `.save_object()` '
        'that is no longer compatible with REST framework 3. '
        'Use the new-style `.create()` and `.update()` methods instead.' %
        (self.__class__.__module__, self.__class__.__name__)
    )

    assert hasattr(self, '_errors'), (
        'You must call `.is_valid()` before calling `.save()`.'
    )

    assert not self.errors, (
        'You cannot call `.save()` on a serializer with invalid data.'
    )

    # Guard against incorrect use of `serializer.save(commit=False)`
    assert 'commit' not in kwargs, (
        "'commit' is not a valid keyword argument to the 'save()' method. "
        "If you need to access data before committing to the database then "
        "inspect 'serializer.validated_data' instead. "
        "You can also pass additional keyword arguments to 'save()' if you "
        "need to set extra attributes on the saved model instance. "
        "For example: 'serializer.save(owner=request.user)'.'"
    )

    assert not hasattr(self, '_data'), (
        "You cannot call `.save()` after accessing `serializer.data`."
        "If you need to access data before committing to the database then "
        "inspect 'serializer.validated_data' instead. "
    )

    validated_data = dict(
        list(self.validated_data.items()) +
        list(kwargs.items())
    )

    if self.instance is not None:
        self.instance = self.update(self.instance, validated_data)
        assert self.instance is not None, (
            '`update()` did not return an object instance.'
        )
    else:
        self.instance = self.create(validated_data)
        assert self.instance is not None, (
            '`create()` did not return an object instance.'
        )
  • self.validated_date와 kwargs를 합쳐서 validated_data에 저장한다
  • self.instance가 있다면 업데이트를 하고 없다면 새로 만들어준다.

댓글

이 블로그의 인기 게시물

[Linux, Unix] export, echo 명령어 알아보기

IEEE 754 부동 소수점 반올림과 근사