-
[Django] DRF Serializer(시리얼라이저) 동작 원리파이썬/Django 2022. 1. 24. 22:44
들어가며
DRF에서 주요 클래스(ViewSet, Router, Serializer) 중 하나인 Serializer의 동작 원리를 살펴본다.
Serializer의 기능은 세 가지가 있는데 다음과 같다.
- 모델 객체 → 파이썬 네이티브 타입인 dict 타입 객체로 직렬화
- dict 타입의 데이터를 validation
- 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": "홍" } """
data
프로퍼티에 접근시 직렬화가 된다.data
는to_representation
메서드를 호출하는데 이 메서드가 실질적으로 직렬화 해주는 작업을 진행한다.to_representation
는write_only=True
인 경우를 제외한 readable 필드를 모두 가져온다.password
는write_only=True
이므로 readable 필드에서 제외된다.dict
타입의 변수(ret
)를 정의한 후에 readable 필드를 이터레이션한다.ret
의 key에는 필드의 이름, value에는 필드의to_representation
메서드의 리턴값을 저장한다. ex)ret[field.fieldname] = field.to_representation()
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
시리얼라이저의 검증 과정은 다음과 같다.
- 비어있어도 되는지 확인
validate_empty_values
메서드를 호출함으로써 이 시리얼라이저에 데이터가None
또는 아예 데이터를 전달받지 않아도 되는지 검증- 각각의 필드를 검증
writable_fields
를 가져와 하나씩 iteration- 각 필드마다
run_validation
을 호출해서 위의 1번부터의 과정을 똑같이 거침 - 각 필드마다 추가로 검증할 수 있는
validate_{필드 이름}
을 호출해서 개발자가 정의한 검증 작업
to_internal_value
메서드를 호출해서 각 필드(username
,password1
,password2
,first_name
,last_name
) 를 검증하는 과정을 거침- 최초 파라미터로 전달받은 리스트 형태의
validators
가 있는 경우 각각 호출해서 확인 - 💬 CharFIeld의 `max_length`나 `min_length` 같은 데이터 길이를 검증할 떄 `MaxLengthValidator` `MinLengthValidator` 같은 `validator`의 호출이 필요하지만 `Serilaizer` 입장에서는 이미 4번에서 충분히 확인할 수 있으므로 메서드의 기능이 불필요하다는 생각이 듦
- 전체적으로 데이터를 확인위의
UserCreateSerializer
경우에는password1
과password2
가 같은지 확인할 필요가 있음 validate
메서드를 호출해서 모든 필드를 종합적으로 검증하는 과정임- 위의 과정을 거치면서 에러들이 발생했고
raise_exception
필드가True
일 경우 에러를 발생시킴
3. dict → Model
validation
이 완료된 이후 해당 데이터를 저장하는save
메서드를 호출하면 개발자가 정의한create
또는update
가 호출되면서 해당하는 오브젝트가 반환된다. 무조건is_valid
를 먼저 호출해서 데이터를 검증한 후에save
메서드를 호출해야 한다. 그렇지 않으면 에러가 발생한다.ModelSerializer
의 경우 알아서 메타클래스에 정의한 모델로 생성 및 업데이트를 해준다.create와 update 호출 되는 조건은 다음과 같다.
data
만 있는 경우:create
instance
와data
가 있는 경우:update
'파이썬 > Django' 카테고리의 다른 글
[Django] DRF Router(라우터) 동작 원리 (0) 2022.02.13 pydantic이란? (DRF Serializer를 대체할 수 있을까?) (1) 2021.12.14 Docker + Django에 Gitlab-CI(Pytest) 붙이기 (0) 2021.10.24 [Django] Django의 동작 원리와 과정 파헤치기 (1) 2021.10.20 Gunicorn 동작 과정 알아보기 (0) 2021.10.10