오늘도 개발

Django Rest Framework 튜토리얼 따라하기 1. Serialization 본문

웹 프로그래밍/Django

Django Rest Framework 튜토리얼 따라하기 1. Serialization

Sueeeeeee 2023. 1. 7. 18:53

serialization(직렬화)이란?

데이터를 웹 통신이 가능한 형식으로 변환하거나,

웹 통신으로 받은 데이터를 프로그래밍 언어에서 사용할 수 형식으로 변환하는 것을 뜻한다.

장고에서 serialization은 파이썬 데이터를 JSON으로, JSON을 파이썬 데이터로 변환하는 것을 말한다.

직렬화가 필요한 이유는 파이썬 데이터를 웹 통신에 바로 사용할 수 없기 때문이다.

 

조립된 1000피스 퍼즐을 친구에게 택배로 보내주어야 한다고 생각해보자.

조립된 형태 그대로 주는 것보다, 분리해서 그림과 함께 상자에 담아 주는 것이 편리하다.

택배를 보내기 위해 퍼즐을 분리하는 것, 택배를 받은 후 조립하는 것이 직렬화이다.

 

models.py

from django.db import models
from pygments.lexers import get_all_lexers
from pygments.styles import get_all_styles

LEXERS = [item for item in get_all_lexers() if item[1]]
LANGUAGE_CHOICES = sorted([(item[1][0], item[0]) for item in LEXERS])
STYLE_CHOICES = sorted([(item, item) for item in get_all_styles()])

class Snippet(models.Model):
    created = models.DateTimeField(auto_now_add=True)
    title = models.CharField(max_length=100, blank=True, default='')
    code = models.TextField()
    linenos = models.BooleanField(default=False)
    language = models.CharField(choices=LANGUAGE_CHOICES, default='python', max_length=100)
    style = models.CharField(choices=STYLE_CHOICES, default='friendly', max_length=100)

    class Meta:
        ordering = ['created']

 

serializers.py

DRF에서 serializer란? 직렬화할 데이터의 형태를 정의한 모양틀이다.

모델을 본따 serializer를 만들 수 있다. 하지만 모델과 serializer가 꼭 일치할 필요는 없다.

다음은 위의 models.py에서 정의한 Snippet 모델과 일치하는 serializer이다.

from rest_framework import serializers
from snippets.models import Snippet, LANGUAGE_CHOICES, STYLE_CHOICES

class SnippetSerializer(serializers.Serializer):
    id = serializers.IntegerField(read_only=True)
    title = serializers.CharField(required=False, allow_blank=True, max_length=100)
    code = serializers.CharField(style={'base_template':'textarea.html'})
    linenos = serializers.BooleanField(required=False)
    language = serializers.ChoiceField(choices=LANGUAGE_CHOICES, default='python')
    style = serializers.ChoiceField(choices=STYLE_CHOICES, default='friendly')
	
     # json 데이터를 파이썬 데이터로 변환한 후 db에 저장할 때 호출됨. 
     # 작성하지 않으면 db 저장 시 오류남.
    def create(self, validated_data):
        return Snippet.obects.create(**validated_data)
	
     # json 데이터를 파이썬 데이터로 변환한 후 db에 저장할 때 호출됨. 
     # 작성하지 않으면 db 저장 시 오류남.
    def update(self, instance, 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

이런 경우 ModelSerializer를 사용하면 코드를 단축할 수 있다.

create, update 메서드도 알아서 구현해주기 때문에 생략해도 된다.

fields에는 serializer에 사용할 모델의 필드를 적어주면 된다.

class SnippetSerializer(serializers.ModelSerializer):
    class Meta:
        model = Snippet 
        fields = ['id', 'title', 'code', 'linenos', 'language', 'style']

serializer의 모든 필드를 보려면(암묵적으로 생성된 필드 포함) repr를 사용하면 된다.

serializer = SnippetSerializer()
print(repr(serializer))

 

쉘에서 오브젝트를 serialization해보기

from snippets.models import Snippet
from snippets.serializers import SnippetSerializer
from rest_framework.renderers import JSONRenderer
from rest_framework.parsers import JSONParser

'''
snippet 오브젝트를 JSON으로 바꿔보기 
'''
snippet = Snippet(code='abc')

# 1. snippet 오브젝트를 serializer에 넣기
serializer = SnippetSerializer(snippet)
print(serializer.data)
# {'id': 2, 'title': '', 'code': 'def', 'linenos': False, 'language': 'python', 'style': 'friendly'}

# 2. 결과물을 JSONRenderer로 json으로 바꾸기
json_data = JSONRenderer().render(serializer.data)
print(json_data)
# b'{"id":2,"title":"","code":"def","linenos":false,"language":"python","style":"friendly"}'
# 장고에서 GET 요청을 받아 클라이언트에게 데이터를 보낼 때 이런 형식으로 보내줘야 한다.

'''
JSON 데이터를 파이썬 오브젝트로 바꿔보기 
'''

import io
stream = io.BytesIO(json_data)
python_data = JSONParser().parse(stream)

# 1. json 데이터를 serializer에 넣기
# data 키워드 인수를 사용해서 넣은 데이터는 사용 전 유효한지 확인하는 과정을 거치게 설계되어 있다.
# 따라서 외부에서 가져온 데이터가 유효한지 확인할 때(POST 요청 등) 사용하면 좋다.
serializer = SnippetSerializer(data=python_data)

# 2. is_valid()로 serializer로 변환된 데이터가 유효한지 검사(검사 안하고 데이터 가져오려고 하면 오류남)
# 유효한 경우 데이터가 validated_data에 저장됨
serializer.is_valid()

# 3. 변환된 데이터 확인
serializer.validated_data

# 4. 변환된 데이터를 db에 저장
# serializer에 정의된 create 메서드가 실행됨
serializer.save()

 

views.py

DRF의 view 기능을 사용하지 않는 버전

from django.http import HttpResponse, JsonResponse
from django.views.decorators.csrf import csrf_exempt
from rest_framework.parsers import JSONParser
from snippets.models import Snippet
from snippets.serializers import SnippetSerializer

@csrf_exempt
def snippet_list(request):
    if request.method == "GET":
        snippets = Snippet.objects.all()
        serializer = SnippetSerializer(snippets, many=True)
        return JsonResponse(serializer.data, safe=False)

    if request.method == "POST":
        data = JSONParser().parse(request)
        serializer = SnippetSerializer(data=data)
        if serializer.is_valid():
            serializer.save()
            return JsonResponse(serializer.data, status=201)
        return JsonResponse(serializer.errors, status=400)

@csrf_exempt
def snippet_detail(request, pk):
    try:
        snippet = Snippet.objects.get(pk=pk)
    except Snippet.DoesNotExist:
        return HttpResponse(status=404)

    if request.method == "GET":
        serializer = SnippetSerializer(snippet)
        return JsonResponse(serializer.data)

    elif request.method == "PUT":
        data = JSONParser().parse(request)
        serializer = SnippetSerializer(data=data)
        if serializer.is_valid():
            # serializer의 create이나 update 메서드가 호출됨
            serializer.save()
            return JsonResponse(serializer.data)
        return JsonResponse(serializer.errors, status=400)

    elif request.method == "DELETE":
        snippet.delete()
        return HttpResponse(status=204)

 

 

참고

https://www.django-rest-framework.org/tutorial/1-serialization/