JSON 직렬화

최동혁·2022년 12월 6일
0

DRF

목록 보기
3/19

JSON 직렬화

직렬화 (Serialization)

모든 프로그래밍 언어의 통신에서 데이터는 필히 문자열로 표현되어야만 합니다.

송신자 : 객체를 문자열로 변환하여, 데이터 전송 → 직렬화

수신자 : 수신한 문자열을 다시 객체로 변환하여, 활용 → 비직렬화

각 언어에서 모두 지원하는 직렬화 포맷 (JSON, XML 등) 도 있고, 특정 언어에서만 지원하는 직렬화 포맷 (파이썬은 Pickle) 이 있습니다.

JSON 변환

데이터가 같아도, 응답 형식이 다를 수 있다.

보통의 웹 세상에서는 …

  • GET 요청에 대해 HTML 포맷으로 응답
  • POST 요청을 application/x-www-form-urlencoded 인코딩 혹은 multipart/form-data 인코딩으로 요청하고, HTML포맷으로 응답

요즘의 API 서버에서는 대개 JSON 인코딩된 요청/응답

rest_framework.renderer.JSONRender

rest_framework/utils/encoders.py의 JSONEncoder를 통한 직렬화

  • 장고의 DjangoJSONEncoder를 상속받지 않고, json.JSONEncoder 상속을 통해 구현
  • datetime.datetime/date/time/timedelta, decimal.Decimal, uuid.UUID, six.binary_type
  • getitem 속성을 지원할 경우 dict(obj) 변환
  • iter 속성을 지원할 경우, tuple 변환
  • QuerySet 타입일 경우, tuple 변환
  • .tolist 속성을 지원할 경우. obj.tolist() 반환

Model 타입은 미지원 → ModelSerializer를 통한 변환

rest_framework.renderer.JSONRenderer

json.dumps에 대한 래핑 클래스 → 보다 편리한 직렬화 지원

UTF8 인코딩도 추가로 수행

from rest_framework.renderers import JSONRenderer

data = Post.objects.all()
JSONRenderer().render(data)

ModelSerializer를 통한 JSON 직렬화

Serializer/ModelSerializer은 Form/ModelForm와 유사

→ 역할 면에서 Serializer는 POST요청만 처리하는 Form

ModelForm과 유사한 ModelSerializer

비교

# forms.py

from django.forms import ModelForm
from .models import Post

class PostForm(ModelForm):
		class Meta:
				model = Post
				fields = '__all__'
# serializers.py

from rest_framework.serializers import ModelSerializer

# Post모델에 대한 ModelSerializer 정의
class PostModelSerializer(ModelSerializer):
		class Meta:
				model = Post
				fields = '__all__'
		
post = Post.objects.first() # Post 타입
serializer = PostModelSerializer(post)
serializer.data # -> ReturnDict 타입

Serializer 활용법

한개의 객체를 넘길 때

여러개의 객체가 있는 쿼리셋을 넘길 때

여기서 serializer.data의 타입은 ReturnDict 타입

ReturnDict 타입이란?

  • serializer.data의 데이터 타입
  • OrderedDict을 상속받았으며, serializer를 키워드 인자로 받는다.
class ReturnDict(OrderDict):
		def __init__(self, *args, **kwargs):
				self.serializer = kwargs.pop('serializer')
				super().__init__(*args, **kwargs)
				....

ModelSerializer의 QuerySet 변환 지원

qs = Post.objects.all()
serializer = PostModelSerializer(qs, many=True)

serializer.data # list와 OrderedDict의 조합

# 파이썬 기본 JSON 변환 사용 활용
import json
json_str_string = json.dumps(serializer.data, ensure_ascii=False)

# DRF에서 지원하는 JSON 변환 활용 à 변환 Rule이 추가된 Encoder
from rest_framework.renderers import JSONRenderer
json_utf8_string = JSONRenderer().render(serializer.data)
  • Model 객체에 대해서는 필히 many=False 지정(디폴트)
  • QuerySet 객체에 대해서는 필히 many=True 지정

→ 지정이 맞지 않으면 변환 에러 발생 (TypeError, AttributeError)

Serializer를 통한 Model Instance → OrderedDict

class Serializer(BaseSerializer, metaclass=SerializerMetaclass):
		# ...
		def to_representation(self, instance):
				"""
				Object instance -> Dict of primitive datatypes.
				"""
				ret = OrderedDict()
				fields = self._readable_fields

				for field in fields:
						try:
								attribute = field.get_attribute(instance)
						except SkipField:
								continue
						# We skip `to_representation` for `None` values so that fields do
						# not have to explicitly deal with that case.
						#
						# For related fields with `use_pk_only_optimization` we need to
						# resolve the pk value.
						check_for_none = attribute.pk if isinstance(attribute, PKOnlyObject) else attribute
						if check_for_none is None:
								ret[field.field_name] = None
						else:
								ret[field.field_name] = field.to_representation(attribute)
		return ret
  • 실제 serializer에서의 직렬화는 인코더를 통한 직렬화라기 보다는, drf 로직으로서 직렬화를 하고 있다.

View에서의 JSON 응답

장고 기본 View에서의 HttpResponse JSON 응답

모든 View는 HttpResponse 타입의 응답을 해야만 한다.

일반적으로 다음 2가지 방법

  1. 직접 json.dumps를 통해 직렬화된 문자열을 획득하여 HttpResponse를 통해 응답
  2. 1번을 정리하여 JsonResponse 지원 → 내부적으로 json.dumps를 사용하며 DjangoJSONEncoder가 디폴트 지정

JsonResponse에서 QuerySet을 JSON 직렬화

이전에 정의한 MYJSONEncoder를 활용

qs = Post.objects.all()

# JsonResponse 생성자의 각종 인자 나열
encoder = MyJSONEncoder
safe = False # True: data가 dict일 경우, False: dict이 아닐 경우
json_dumps_params = {'ensure_ascii': False}
kwargs = {} # HttpResponse에 전해지는 Keyword 인자

from Django.http import JsonResponse

response = JsonResponse(qs, encoder, safe, json_dumps_params, **kwargs)

DRF를 통한 JSON 응답

DRF를 통한 HttpResponse JSON 응답

DRF Response 활용

qs = Post.objects.all()

serializer = PostModelSerializer(qs, many=True)

from rest_framework.response import Response
response = Response(serializer.data) # Content-Type: text/html 디폴트 지정

Response와 APIView

DRF의 모든 뷰는 APIView를 상속받는다.

APIView를 통해 Response에 다양한 속성이 지정된다.

from rest_framework.views import APIView

renderer_cls = APIView.renderer_classes[0]
renderer_obj = renderer_cls()
response.accepted_renderer = renderer_obj # JSON 변환을 위한 JSONRenderer 인스턴스

response.accepted_media_type = renderer_obj.media_type # 'application/json'
response.renderer_context = {'view': None, 'args': (), 'kwargs': {}, 'request': None}

response # <Response status_code=200, "application/json">

실제 DRF Serializer 활용

다음과 같이 간결하게 사용

from rest_framework import generics

class PostListAPIView(generics.ListAPIView):
		queryset = Post.objects.all()
		serializer_class = PostModelSerializer

post_list = PostListAPIView.as_view()
  • generics의 ListAPIView는 List 기능만 지원한다.
  • 만약 List, Create, Delete, Update, Get 기능들도 다 쓰고 싶다면 ViewSet 사용
  • ListCreateAPIView는 List와 Create 두가지 기능을 해줌.

포스팅 조회 응답에 username 응답을 하려면?(1/2)

author = FK(User) 필드가 있을 때, Serializer에서는 FK 키값으로 응답

serializer.ReadOnlyField를 통해 FK의 필드값을 읽어올 수 있다.

from rest_framework import serializers
from .models import Post

class PostSerializer(serializers.ModelSerializer):
		username = serializers.ReadOnlyField(source='author.username') # 추가

		class Meta:
				model = Post
				fields = ['pk', 'username', 'title', 'cotnent'] # 'username' 추가

포스팅 조회 응답에 username 응답을 하려면?(2/2)

StringRelatedField, SlugRelatedField 등 뿐만 아니라, 중첩된 Serializer를

통해서도 구현 가능

from django.contrib.auth import get_user_model
from rest_framework import serializers
from .models import Post

class AuthorSerializer(serializers.ModelSerializer):
		class Meta:
				model = get_user_model()
				fields = ['username']

class PostSerializer(serializers.ModelSerializer):
		author = AuthorSerializer()

		class Meta:
				model = Post
				fields = '__all__'
profile
항상 성장하는 개발자 최동혁입니다.

0개의 댓글