직렬화 ( Serializer )
모든 프로그래밍 언어의 통신에서 데이터는 문자열로 표현되어야만 한다
직렬화에 대해 항상 비직렬화 규칙도 갖고 있어야 원래 객체 복원 가능하다
- 송신자 : 직렬화 (Serialize) = 객체를 문자열으로 변환하여 데이터 전송
- 수신자 : 비직렬화 (deserialize) = 수신한 문자열을 다시 객체로 변환하여 활용
직렬화 포맷의 종류
- 각 언어에서 모두 지원 : JSON, XML 등
- 특정 언어에서만 지원 : Pickle ( 파이썬 ), 직접 만든 커스텀 포맷 등 → 타 언어로 전달했을 때 비직렬화 에러 발생
- 보통의 웹 : GET 요청에 대해선 HTML 포맷으로 응답 / POST 요청에 대해선 application/x-www-form-urlencoded 나 multipart/form-data로 인코딩하고 HTML 포맷으로 응답
- API 서버 : 대게 JSON 인코딩된 요청과 응답
→ 같은 데이터라도 응답 형식이 다를 수 있다
JSON 포맷
- 다른 언어/플랫폼과 통신할 때 주로 사용
- 파이썬 표준 라이브러리 json 제공 → 파이썬 기본 데이터 타입 외 ( Model, QuerySet 등) 에 대해서는 직렬화 룰이 없다
- pickle에 비해 직렬화를 지원하는 데이터타입의 수가 적지만, 커스텀 룰 지정 가능
import json
# 데이터 준비
post_list = [
{'message' : 'hello askdjango' }
]
# 직렬화
json_string = json.dumps(post_list)
print(json_string) # [{ "message" : "hello askdjango" }] 로 출력
# 비직렬화
print(json.loads(json_string))
json의 기본 타입은 파이썬3 기준으로 str 이다
실제로 파일로 저장할때는 UTF-8 인코딩을 통해 bytes로 변환해야 한다
타입 에러(TypeError)가 나는 경우
python manage.py shell
>> import json
>> from django.contrib.auth import get_user_model
>> User = get_user_model()
>> json.dumps(User.objects.first())
위에서 언급했듯 json은 파이썬 기본 라이브러리이므로 장고 타입(Model, QuerySet) 에 대해선 직렬화 룰이 없기 때문에 에러가 난다.
해결방안 : 두 가지 다 파이썬 기본에서 지원해주는 직렬화 방법 사용
(1) 직접 변환
from django.core.serializers.json import DjangoJSONEncoder
data = [
{'id' : post.id, 'title' : post.title, 'content' : post.content }
for post in Post.objects.all()
]
json.dumps( data )
(2) 직접 변환 rule 지정하기
import json
from django.core.serializers.json import DjangoJSONEncoder
from django.db.models.query import QuerySet
class MyJSONEncoder( DjangoJSONEncoder ) :
def default( self, obj ):
if isinstance(obj, QuerySet ):
return tuple(obj)
elif isinstance(obj, Post):
return {'id':obj.id, 'title': obj.title, 'content': obj.content}
elif hasattr(obj, 'as_dict'):
return obj.as_dict()
return super().defualt(obj)
data = Post.objects.all()
# 직렬화를 수행해줄 JSONEncoder 지정해준다
json.dumps(data, cls=MyJSONEncoder)
※ DjangoJSONEncoder : 기본 json 라이브러리에 있는 기본 인코더를 상속받아서 몇가지 타입에 대한 직렬화 룰 추가한 것
JSONRender
rest_framework.renderer.JSONRender
장고의 DjangoJSONEncoder을 상속받지 않고, json.JSONEncoder 상속을 통해 구현
But, Model 타입에 대한 직렬화는 지원하지 X
JSONRenderer
rest_framework.renderer.JSONRenderer
json.dumps을 래핑한 클래스로 보다 편리한 직렬화를 제공한다. UTF-8인코딩도 추가로 수행한다
But, Model 타입에 대한 직렬화는 지원하지 X
ModelSerializer
Serializer/ModelSerializer 은 Form/ModelForm과 유사하다.
Serializer 는 POST 요청만 처리하는 Form 이라고 생각할 수 있지만 기능이 좀 더 많다
장고 Form/ModelForm | DRF Serializer/ModelSerializer |
폼 필드드를 지정하거나 모델로부터 필드정보를 읽어와 form 필드와 serializer필드를 채워준다 | |
Form 태그가 포함된 HTML 생성 | Form 데이터가 포함된 JSON 문자열을 생성 |
입력된 데이터에 대한 유효성 검사 및 획득 |
- Model 객체 : many=False 지정 (defualt)
- QuerySet 객체 : many=True 지정
→ 지정이 맞지 않으면 에러 발생
from rest_framework.serializers import ModelSerializer
class PostModelSerializer(ModelSerializer):
class Meta:
model = Post
fields = '__all__'
post = Post.objects.first() # post는 모델 객체
serializer = PostModelSerializer(post)
# serializer.data 는 ReturnDict 타입 객체
qs = Post.objects.all() # qs는 QuerySet 객체
serializer2 = PostModelSerializer(qs, many=True )
# serializer.data 는 list와 OrderDict의 조합인 객체
# (1)파이썬 기본 JSON 변환 활용
import json
json_str_string = json.dumps(serializer2.data)
# (2)DRF에서 지원하는 JSON 변환 활용
form rest_framework.renderers import JSONRenderer
json_utf8_string = JSONRenderer().render(serializer2.data)