직렬화 (Serialize) : 모델 인스턴스나 QuerySet과 같은 데이터를 JSON 형식의 파일로 변환하는 작업
역직렬화 (Deserialize) : JSON 형식의 데이터를 정의된 포맷에 맞춰 다시 모델 인스턴스로 변환하는 작업
JSONRenderer()
는 Serialize된 데이터를 JSON 형식으로 렌더링한다.
json.loads()
는 JSON 형식의 데이터를 딕셔너리 형태로 Deserialize한다.
Serializer는 데이터를 직렬화(Serialize)하고 역직렬화(Deserialize)하기 위한 도구이다. 모델 인스턴스를 JSON 형식으로 변환할 수 있고 주로 RESTful API에서 데이터를 전송할 때 사용된다.
djangorestframework
의 설치가 필요하다. 아래의 명령어로 설치하고 python 코드에서 import하여 사용한다.
pip install djangorestframework
serializers
는 데이터를 저장/수정할 때 항상 데이터의 유효성을 검사하고, 검사를 통과해야만 저장이 가능하다.
from rest_framwork import serializers
from polls.models import Question # serialize 할 모델 가져오기
class QuestionSerializer(serializers.Serializer):
id = serializers.IntegerField(read_only=True)
question_text = serializers.CharField(max_length=200)
pub_date = serializers.DateTimeField(read_only=True)
# 유효성 검사를 통과한 데이터를 기반으로 저장
def create(self, validated_data):
return Question.objects.create(**validated_data)
def update(self, instance, validated_data):
# validated_data는 ordered dict 형태로 .get() 메소드를 사용할 수 있다
instance.question_text = validated_data.get('question_text', instance.question_text)
instance.save()
return instance
serializers.Serializer
를 사용하여 모델의 Serializer 클래스를 구현할 수도 있지만 serializers.ModelSerializer
를 사용한다면 좀 더 편리하게 구현할 수 있다.
구현할 클래스 내의 Meta
클래스 내부의 model
속성을 통해 Serialize할 대상 모델을 지정할 수 있고 fields
속성을 통해 Serialize할 대상 필드들을 리스트 형태로 나열하여 지정할 수 있다.
기본적으로 create()
와 update()
메서드가 구현되어 있어, 추가로 코드를 작성해주지 않아도 그 기능을 사용할 수 있다.
from rest_framework import serializers
from polls.models import Question
class QuestionSerializer(serializers.ModelSerializer):
class Meta:
model = Question
fields = ['id','question_text', 'pub_date']
주로 사용되는 HTTP Method는 아래와 같다. CRUD 기능을 구현할 수 있다.
POST
GET
PUT
DELETE
view에서 HTTP Method를 구현하는 방법은 아래와 같이 다양한 방법이 있다. 복잡한 방법부터 간단한 방법 순서로 질문리스트를 보여주는 view를 구현하는 과정을 알아가 본다.
@api_view
데코레이터 사용APIView
클래스를 상속받아 클래스 기반의 뷰 생성mixins
모듈 사용generics
모듈 사용@api_view
데코레이터의 ()가 비어있는 경우에는, GET
요청을 처리한다는 의미이다. 다른 HTTP Method의 사용을 원할 때는 @api_view('GET','POST')
와 같이 명시해주어야 한다. 여러개의 HTTP Method를 사용할 경우 if 문으로 분기해준다.
지난 번 구현했던 view와 구분하기 위해 아래와 같이 mysite/urls.py
에 polls_api
의 url을 따로 추가해주었다.
# mysite/urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('polls/', include('polls.urls')),
path('rest/', include('polls_api.urls')),
]
polls_api
앱에도 url 파일을 만들어 경로를 찾아갈 수 있게 한다. @api_view
데코레이터를 사용했지만 polls
앱의 url과 달라진 점은 없다.
# polls_api/urls.py
from django.urls import path
from .views import *
# @api_view 데코레이터 사용
urlpatterns = [
path('question/', question_list, name='question-list'),
path('question/<int:id>/', question_detail, name='question-detail')
]
# polls_api/views.py
from polls.models import Question
from polls_api.serializers import QuestionSerializer
from rest_framework.response import Response
from rest_framework.decorators import api_view
@api_view('GET','POST')
def question_list(request):
if request.method == 'GET':
questions = Question.objects.all()
serializer = QuestionSerializer(questions, many = True)
return Response(serializer.data)
if request.method == 'POST':
serializer = QuestionSerializer(data=request.data)
if serializer.is_valid(): # is_valid() 메서드는 serializer가 올바른 데이터 형식을 가지고 있는지 확인하고, 필수 필드가 존재하는지 여부를 검증한다.
serializer.save() # save() 메서드가 실행되면, 데이터베이스에 새로운 질문 객체가 저장된다.
return Response(serializer.data, status=status.HTTP_201_CREATED)
else:
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) # serializer가 유효하지 않은 경우, 에러메시지와 함께 HTTP 응답이 반환한다.
APIView
클래스를 상속받아 클래스 기반의 View 생성클래스 기반의 view에서는 @api_view()
데코레이터를 사용하여 처리할 수 있는 request의 종류 (HTTP method)를 표시해 줄 필요가 없다.
# polls_api/views.py
from rest_framework.views import APIView
class QuestionList(APIView):
def get(self, request):
questions = Question.objects.all()
serializer = QuestionSerializer(questions, many=True)
return Response(serializer.data)
def post(self, request):
serializer = QuestionSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST
함수 기반 뷰와 달리 urls.py
에서 클래스 기반의 뷰의 URL을 정의할 때는 클래스 이름 뒤에 .as_view()
를 붙여서 적어주어야 한다.
# polls_api/urls.py
from django.urls import path
from .views import *
# API_View 클래스 상속한 클래스 사용
urlpatterns = [
path('question/', QuestionList.as_view(), name='question-list'),
path('question/<int:id>/', QuestionDetail.as_view(), name='question-detail'),
]
mixins
모듈 사용mixins
의 모델 mixin클래스를 상속한 클래스로 view를 구현한다. *args
, **kwargs
인자를 받아오고 넘긴다. 구현이 필요없이 request요청별로 정해진 함수를 리턴한다.
# polls_api/views.py
from polls.models import Question
from polls_api.serializers import QuestionSerializer
from rest_framework import mixins
from rest_framework import generics
class QuestionList(mixins.ListModelMixin,
mixins.CreateModelMixin,
generics.GenericAPIView):
queryset = Question.objects.all()
serializer_class = QuestionSerializer
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
class QuestionDetail(mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
generics.GenericAPIView):
queryset = Question.objects.all()
serializer_class = QuestionSerializer
def get(self, request, *args, **kwargs):
return self.retrieve(request, *args, **kwargs)
def put(self, request, *args, **kwargs):
return self.update(request, *args, **kwargs)
def delete(self, request, *args, **kwargs):
return self.destroy(request, *args, **kwargs)
클래스 기반의 뷰의 URL을 정의할 때와 유사하지만
'question/<int:pk>/'
와 같이 id
대신에 pk
라고 명시해야 한다.
# polls_api/urls.py
from django.urls import path
from .views import *
# mixins 사용
urlpatterns = [
path('question/', QuestionList.as_view(), name='question-list'),
path('question/<int:pk>/', QuestionDetail.as_view(), name='question-detail'),
]
generics
모듈 사용generics
의 APIView클래스를 상속받아 구현한다. 클래스 안에 queryset
과 serializer_class
의 값을 넣어주면 되어 가장 간단하다. 😎
polls_api/urls.py
는 mixins
모듈 사용 방법과 동일하게 사용한다.
# polls_api/views.py
from polls.models import Question
from polls_api.serializers import QuestionSerializer
from rest_framework import generics
class QuestionList(generics.ListCreateAPIView):
queryset = Question.objects.all()
serializer_class = QuestionSerializer
class QuestionDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = Question.objects.all()
serializer_class = QuestionSerializer