본문 바로가기

개발일지/TIL

TIL 23-05-12 drf 팀프로젝트 - serializer update(), ininstance

1.drf 팀프로젝트 - serializer update(), ininstance

 문제점

serializer를 이용해서 데이터 베이스의 정보를 업데이트 해야함 

 시도해 본 것들

drf 공식문서 찾아보기 

update()함수 찾기

def update(self, instance, validated_data):
        """
        Update and return an existing `Snippet` instance, given the validated data.
        """
        instance.title = validated_data.get('title', instance.title)
        instance.code = validated_data.get('code', instance.code)
        instance.linenos = validated_data.get('linenos', instance.linenos)
        instance.language = validated_data.get('language', instance.language)
        instance.style = validated_data.get('style', instance.style)
        instance.save()
        return instance

개인 프로젝트 때 create()함수만 사용해보고 update()는 사용해본 적이 없었는데 이번 기회에 알아봤다. 

 

update()함수 파고들어가기 

def update(self, instance, validated_data):
        raise_errors_on_nested_writes('update', self, validated_data)
        info = model_meta.get_field_info(instance)

 

raise_errors_on_nested_writes라는 함수 확인하기 

def raise_errors_on_nested_writes(method_name, serializer, validated_data):
    ModelClass = serializer.Meta.model
    model_field_info = model_meta.get_field_info(ModelClass)

    # Ensure we don't have a writable nested field. For example:
    #
    # class UserSerializer(ModelSerializer):
    #     ...
    #     profile = ProfileSerializer()
    assert not any(
        isinstance(field, BaseSerializer) and
        (field.source in validated_data) and
        (field.source in model_field_info.relations) and
        isinstance(validated_data[field.source], (list, dict))
        for field in serializer._writable_fields
        ), (
        'The `.{method_name}()` method does not support writable nested '
        'fields by default.\nWrite an explicit `.{method_name}()` method for '
        'serializer `{module}.{class_name}`, or set `read_only=True` on '
        'nested serializer fields.'.format(
            method_name=method_name,
            module=serializer.__class__.__module__,
            class_name=serializer.__class__.__name__
        )
    )

여기서 instance는 아니지만 isinstance라는 함수가 보였다. 우선 assert은 모르지만 not any는 하나라도 False라면 밑의 메시지가 띄워지는 것 같다. 

 

isinstance확인하기 

def isinstance(__obj: object, __class_or_tuple: _ClassInfo) -> bool: ...
def issubclass(__cls: type, __class_or_tuple: _ClassInfo) -> bool: ...
def len(__obj: Sized) -> int: ...
def license() -> None: ...
def locals() -> dict[str, Any]: ...

class map(Iterator[_S], Generic[_S]):

무슨 말인지 하나도 모르겠지만 len과 map이 눈에 띄었다. 그래서 파일을 살펴보니 python의 내장 함수 파일이었다.

isinstance가 내장 함수라면 설명이 있을 것이라 판단하고 찾아봤다.

 

isinstance검색하기 

print(isinstance(1234, int))  # True
print(isinstance("1234", int))  # False

isinstance는 주어진 변수의 타입을 True, False로 반환해주는 함수였다. 

 

raise_errors_on_nested_writes함수 코드 다시 살펴보기 

isinstance(field, BaseSerializer)

이렇게 떼어놓고 보니 간단하다.

field가 BaseSerializer의 type과 일치하는지 확인한다. 

 

save()함수 찾아보기 

def save(self, **kwargs):
        """
        Save and return a list of object instances.
        """
        # 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)'.'"
        )

        validated_data = [
            {**attrs, **kwargs} for attrs in self.validated_data
        ]

        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.'
            )

        return self.instance

대부분 무슨소린지 모르겠지만 if문은 눈에 들어왔다.  

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.instance가 있다면 update가 실행되고, 없다면 create가 실행 되는 것으로 보인다. 그래서 궁금했던 내가 view에서 사용하는 것은 save()인데 create()와 update()가 알아서 구분되는 구조를 알 수 있었다. 

가장 궁금했던 instance는 찾지 못했지만 print()를 통해서 확인한 결과 db의 값이 그대로 나오는 것을 알 수 있었고, create()와 update()의 차이를 알게 되면서 이해할 수 있었다. 

다만 print(instance)를 했을 때는 email이 나오고 있었는데 지금 그 원인을 알게 된 것 같다. 

BaseUserManger 를 사용하면서 기본적으로 email을 사용하고 그 값이 unique값이므로 instance가 이메일을 가르키고 있던 것 같다. 

 

 알게 된 점

serializer의 save()가 create()와 update()를 구분하는 기준을 알 수 있었다.

serializer의 update()함수에 전달되는 instance라는 변수가 db의 값을 받아오는 것을 알 수 있었다. 

isinstance라는 파이썬 내장함수의 사용법을 알 수 있었다. 

아직도 모르는게 더 많지만 조금이라도 아는 부분을 기준으로 이해를 넓일 수 있는 경험을 할 수 있었다. 

실제로 코드를 사용하기 위해서 찾아보면 확실히 더 많은 노력을 기울일 수 있는 것을 지금 프로젝트를 하면서 느꼈다.