ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Django] DRF Serializer(시리얼라이저) 동작 원리
    파이썬/Django 2022. 1. 24. 22:44

    들어가며

    DRF에서 주요 클래스(ViewSet, Router, Serializer) 중 하나인 Serializer의 동작 원리를 살펴본다.

    Serializer의 기능은 세 가지가 있는데 다음과 같다.

    1. 모델 객체 → 파이썬 네이티브 타입인 dict 타입 객체로 직렬화
    2. dict 타입의 데이터를 validation
    3. dict 타입 → 모델 객체로 역 직렬화

     

    validation 기능은 주로 역 직렬화 전에 dict 타입의 데이터를 검증해주는 기능으로 사용된다.

    이 Seirlaizer의 세 기능을 각각 어떻게 동작하는지 알아본다.

    1. Model → dict

    class UserSerializer(serializers.Serializer):
        username = serializers.CharField()
        password = serializers.CharField(write_only=True)
        first_name = serializers.CharField(read_only=True)
        last_name = serializers.CharField(read_only=True)
    
    serialized_data = UserSerializer(user).data
    """
    {
        "username": "wookkl",
        "first_name": "길동",
        "last_name": "홍"
    }
    """
    1. data 프로퍼티에 접근시 직렬화가 된다.
    2. datato_representation 메서드를 호출하는데 이 메서드가 실질적으로 직렬화 해주는 작업을 진행한다.
    3. to_representationwrite_only=True 인 경우를 제외한 readable 필드를 모두 가져온다.
    4. passwordwrite_only=True 이므로 readable 필드에서 제외된다.
    5. dict 타입의 변수(ret)를 정의한 후에 readable 필드를 이터레이션한다.
    6. ret의 key에는 필드의 이름, value에는 필드의 to_representation 메서드의 리턴값을 저장한다. ex) ret[field.fieldname] = field.to_representation()
    7. ret 리턴

     

    여기서 만약 필드에 nested serializer가 있으면 어떻게 될까?

    위의 과정에서 알 수 있듯이 serializer과 field는 둘 다 to_representation 메서드를 가지고 있는데 serializer의 필드에 serializer가 정의되어 있더라도 to_representation 메서드를 호출하기 때문에 문제 없다.

    왜냐하면 Serializer는 모든 필드(CharField, IntergerField, ...)의 부모 클래스인 Field 클래스를 상속받기 떄문이다.

     

    따라서 재귀적으로 메서드가 호출되어 직렬화가 이루어지는 것이다.

    코드로 보면

    class JobSerializer(serializers.Serializer):
        name = serializers.CharField(read_only=True)
    
    class UserSerializer(serializers.Serializer):
        ... # 이전과 동일
        job = serializers.JobSerializer(read_only=True)
    
    serialized_data = UserSerializer(user).data
    """
    {
        "username": "wookkl",     # CharField.to_representaion
        "first_name": "길동",      # CharField.to_representaion
        "last_name": "홍",        # CharField.to_representaion
        "job": {               # JobSerializer.to_representaion
            "name": "Developer",      # CharField.to_representaion
        }
    }
    """

    2. Validation

    생성하거나 수정하기 위해 전달된 데이터를 검증하는 작업으로 is_valid 라는 메서드를 통해 검증한다.

    class UserCreateSerializer(serializers.Serializer):
        username = serializers.CharField()
        password1 = serializers.CharField(write_only=True)
        password2 = serializers.CharField(write_only=True)
        first_name = serializers.CharField()
        last_name = serializers.CharField()
    
    data = {
        "username": "wookkl",
        "password" "password123",
        "first_name": "길동", 
        "last_name": "홍",   
    }
    serializer = UserSerializer(data=data)
    serializer.is_valid(raise_exception=True) # raise_exception default는 False

    시리얼라이저의 검증 과정은 다음과 같다.

    1. 비어있어도 되는지 확인
    2. validate_empty_values 메서드를 호출함으로써 이 시리얼라이저에 데이터가 None 또는 아예 데이터를 전달받지 않아도 되는지 검증
    3. 각각의 필드를 검증
      1. writable_fields를 가져와 하나씩 iteration
      2. 각 필드마다 run_validation을 호출해서 위의 1번부터의 과정을 똑같이 거침
      3. 각 필드마다 추가로 검증할 수 있는 validate_{필드 이름}을 호출해서 개발자가 정의한 검증 작업
    4. to_internal_value메서드를 호출해서 각 필드(username, password1,password2, first_name, last_name ) 를 검증하는 과정을 거침
    5. 최초 파라미터로 전달받은 리스트 형태의 validators 가 있는 경우 각각 호출해서 확인
    6. 💬 CharFIeld의 `max_length`나 `min_length` 같은 데이터 길이를 검증할 떄 `MaxLengthValidator` `MinLengthValidator` 같은 `validator`의 호출이 필요하지만 `Serilaizer` 입장에서는 이미 4번에서 충분히 확인할 수 있으므로 메서드의 기능이 불필요하다는 생각이 듦
    7. 전체적으로 데이터를 확인위의 UserCreateSerializer 경우에는 password1password2 가 같은지 확인할 필요가 있음
    8. validate 메서드를 호출해서 모든 필드를 종합적으로 검증하는 과정임
    9. 위의 과정을 거치면서 에러들이 발생했고raise_exception 필드가 True일 경우 에러를 발생시킴

    3. dict → Model

    validation 이 완료된 이후 해당 데이터를 저장하는 save 메서드를 호출하면 개발자가 정의한 create 또는 update 가 호출되면서 해당하는 오브젝트가 반환된다. 무조건is_valid를 먼저 호출해서 데이터를 검증한 후에 save 메서드를 호출해야 한다. 그렇지 않으면 에러가 발생한다.

    ModelSerializer의 경우 알아서 메타클래스에 정의한 모델로 생성 및 업데이트를 해준다.

    create와 update 호출 되는 조건은 다음과 같다.

    data만 있는 경우: create

    instancedata가 있는 경우: update

    댓글

Designed by Tistory.