Serializer

안재영·2024년 4월 10일

serialize

모델 인스턴스나 querySet과 같은 데이터를 JSON형식의 파일로 변환하는변환

deserialize

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에서 작동시켜봅시다

시리얼라이즈를 진행합니다

Serialize

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형식으로 반환됩니다

이번엔 반대로 디시리얼라이즈를 진행합니다

Deserialize

저장

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 확인하면 데이터가 들어있지않은것을 확인할수 있습니다

ModelSerializer

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클래스에서 이미 구현해놨기 때문에 상속만 받으면 해당기능을 간단하게 사용할수 있는것입니다

0개의 댓글