모델 인스턴스나 querySet과 같은 데이터를 JSON형식의 파일로 변환하는변환
JSON형식의 데이터를 정해진 포맷에 맞추어 다시 모델 인스턴스로 변환하는 작업
이러한 serialize와 deserialize작업을 해주는것을 Serializer라고 부릅니다
Django프로젝트를 예시로 serializer를 생성해보겠습니다
일단 polls_api라는 app을 추가로 생성해줍시다
생성이 완료됬다면 serializers.py를 생성해줍니다
from rest_framework import serializers
from ..polls.models import Question
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):
instance.question_text = validated_data.get('question_text', instance.question_text)
instance.save()
return instance
id, question_text, pub_date는 question 모델에서 사용하는 필드들이며 id와 pub_date는 자동으로 입력이 되기때문에 read_only=True로 설정해줍니다
validated_data 는 위field에 조건에 맞는 값인지를 확인하고 값을 넘겨줍니다
create는 validated_data에서 넘겨준 값으로 Question object를 create하여 데이터를 저장하고
update는 기존있는값을 instance에 받아오고 validated_data를 받아옵니다 그후 instance의 question_text를 validated의 question_text로 수정하지만 없으면 instance.question이 다시 들어가며해당내용을 저장합니다
시리얼라이저를 생성하였으니 Django Shell에서 작동시켜봅시다
시리얼라이즈를 진행합니다
from polls.models import Question
from polls_api.serializers import QuestionSerializer
from rest_framework.renderers import JSONRenderer
import json
q = Question.objects.first()
serializer = QuestionSerializer(q)
json_str = JSONRenderer().render(serializer.data)
data = json.loads(json_str)
위 코드를 실행하면 question의 첫번째 값이 json형식으로 반환됩니다
이번엔 반대로 디시리얼라이즈를 진행합니다
serializer = QuestionSerializer(data=data)
serializer.is_valid()
serializer.validated_data
new_question = serializer.save()
data={'question_text': '제목수정'}
serializer = QuestionSerializer(new_question, data=data)
serializer.is_valid() # is_valid 로 확인하면 적합성을 확인할수 있습니다
serializer.save()
이번에는 json형식의 데이터로 모델을 이용하여 데이터의 추가 수정작업을 진행하였습니다
long_text = "abcd"*300
data = {'question_text':long_text}
serializer = QuestionSerializer(data=data)
serializer.is_valid()
serializer.validated_data
serializer.is_valid() 는 False 가 나오며
serializer.validated_data 확인하면 데이터가 들어있지않은것을 확인할수 있습니다
Serializer.py의 내용을 수정해줍시다
class QuestionSerializer(serializers.ModelSerializer) :
class Meta:
model = Question
fields=['id','question_text', 'pub_date']
ModelSerializer를 사용하면 기존 필드를 하나하나 정의 해준것에 비해 이번엔 Meta안에 question모델과 fields를 받아와 간단하게 필드들을 모델에 조건에 맞춰 잡아 줄 수 있습니다. 또한 create와 update도 자체적으로 구현되어있습니다
위 내용들을 이용해 restapi를 구현해봅시다
poll_api에 urls.py를 생성해 url을 처리해줍시다
from django.urls import path
from .views import *
urlpatterns = [
path('question/', question_list, name='question-list')
]
poll_api에 views.py를 수정하여 url에서 요청할 작업들을 처리해줍시다
from django.shortcuts import render
from rest_framework.decorators import api_view
from polls.models import Question
from .serializers import QuestionSerializer
from rest_framework.response import Response
# Create your views here.
@api_view()
def question_list(request):
questions = Question.objects.all()
serializer = QuestionSerializer(questions, many = True)
return Response(serializer.data)
QuestionSerializer(questions, many = True)시리얼라이저에 여러개의 값을 넣을경우는 many속성을 True로 설정해줘야 합니다.
questions는 Question의 모든 object가 들어있으므로 many=True를 추가해줍시다
이제 프로젝트폴더의 url를 수정해줍시다
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')),
]
path("rest/", include('polls_api.urls')), 가 추가되어 rest로 접근시 polls_api의urls로 넘어가게 처리하였으니 작성한 파일들로 잘 넘어갈것입니다
이제 확인을 위해 서버를 켜서 주소/rest/question으로 접근해봅시다
만일 정상적으로 접근이안되고 오류가발생할시는 settings부분에 rest_framework가 등록이 안되 생기는 오류일수있습니다
INSTALLED_APPS = [
'polls.apps.PollsConfig',
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
'rest_framework',
]
화면이 정상적으로 나온다면 화면에 question의 데이터가 json형식으로 출력되는것을 볼수 있습니다
@api_view을 조금더 다뤄봅시다
from django.shortcuts import render
from rest_framework.decorators import api_view
from polls.models import Question
from .serializers import QuestionSerializer
from rest_framework.response import Response
# Create your views here.
@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() :
serializer.save()
return Response(serializer.data)
else :
return Response(serializer.errors)
@api_view(['GET', 'POST'])로 받을수있는 request 메소드를 설정할수 있습니다
get방식으로 접근하면 question의 모든 값을 넘겨주고
post방식으로 접근하면 해당 데이터를 저장한뒤 error나 data로 응답합니다
하지만 errors시에도 status_code를 확인하면 200으로 요청에 응답을 성공했다는 코드를 반환합니다. 코드에 오류가 발생하지는 않았지만 저장에는 실패했기때문에 해당코드가 200이 아닌 400으로 반환하게 처리해봅시다
from django.shortcuts import render
from rest_framework.decorators import api_view
from polls.models import Question
from .serializers import QuestionSerializer
from rest_framework.response import Response
from rest_framework import status
# Create your views here.
@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() :
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
else :
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
이제 성공시에는 생성에 성공했다는 201코드와 실패시는 400코드를 응답하게됩니다
이번에는 method가 아닌 class로 api로 처리해봅시다
class QuestionDetail(APIView):
def get(self, request, id):
question = get_object_or_404(Question, pk=id)
serializer = QuestionSerializer(question)
return Response(serializer.data)
def put(self, request, id):
question = get_object_or_404(Question, pk=id)
serializer = QuestionSerializer(question, data=request.data)
if serializer.is_valid() :
serializer.save()
return Response(serializer.data, status=status.HTTP_200_OK)
else :
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
def delete(self, request, id):
question = get_object_or_404(Question, pk=id)
question.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
urls.py
urlpatterns = [
path('question/', question_list, name='question-list'),
path('question/<int:id>', QuestionDetail.as_view(), name='question-detail'),
]
APIView는 요청 mathod를 받아 해당 mathod에 맞는 함수를 실행하여 작동하게 됩니다
그외는 그전 방식과 동일하게 돌아갑니다
클래스를 이용하면 장점은 rest_framework에서 제공하는 다양한 프레임워크를 이용할수 있다는 장점이 있습니다
해당 코드를 추가로 수정해봅시다
mixin을 사용하면 정말 간단한 코드로 수정을 할 수 있습니다
from rest_framework import status, mixins, generics
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)
urlpatterns = [
path('question/', question_list, name='question-list'),
path('question/<int:pk>', QuestionDetail.as_view(), name='question-detail'),
]
url에 <int:id>부분을 <int:pk>로 바꿔줍시다
generics를 사용한다면 이것보다 더 간단한 코드로 줄일수있습니다
class QuestionDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = Question.objects.all()
serializer_class = QuestionSerializer
이렇게 간단하게 처리할수 있는 이유는 mixins 또는 generics클래스에서 이미 구현해놨기 때문에 상속만 받으면 해당기능을 간단하게 사용할수 있는것입니다