파이썬 장고 프레임워크를 사용해서 API 서버 만들기 (3)

PH_Lee·2024년 4월 10일

Serilaizer

모델 인스턴스나 QuerySet과 같은 데이터를 Jason 형식의 파일로 변환하는 작업
Deserialize : Jason 형식의 데이터를 정의된 포맷에 맞추어 다시 모델 인스턴스로 변환하는 작업

polls_api/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

Django Shell에서 Serializer 사용하기

polls_api/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

Django Shell

#Serialize
>>> from polls.models import Question
>>> from polls_api.serializers import QuestionSerializer
>>> q = Question.objects.first()
>>> q 
<Question: 제목: 휴가를 어디서 보내고 싶으세요?, 날짜: 2023-02-05 18:52:59+09:00>
>>> serializer = QuestionSerializer(q)
>>> serializer.data
{'id': 1, 'question_text': '휴가를 어디서 보내고 싶으세요?', 'pub_date': '2023-02-05T18:52:59Z'}
>>> from rest_framework.renderers import JSONRenderer
>>> json_str = JSONRenderer().render(serializer.data)
>>> json_str
b'{"id":1,"question_text":"\xed\x9c\xb4\xea\xb0\x80\xeb\xa5\xbc \xec\x96\xb4\xeb\x94\x94\xec\x84\x9c \xeb\xb3\xb4\xeb\x82\xb4\xea\xb3\xa0 \xec\x8b\xb6\xec\x9c\xbc\xec\x84\xb8\xec\x9a\x94?","pub_date":"2023-02-05T18:52:59Z"}'

# Deserialize
>>> import json
>>> data = json.loads(json_str)
>>> data
{'id': 1, 'question_text': '휴가를 어디서 보내고 싶으세요?', 'pub_date': '2023-02-05T18:52:59Z'}
>>> serializer = QuestionSerializer(data=data)
>>> serializer.is_valid()  
True
>>> serializer.validated_data 
OrderedDict([('question_text', '휴가를 어디서 보내고 싶으세요?')])
>>> new_question = serializer.save() # Create
>>> new_question
<Question: 제목: 휴가를 어디서 보내고 싶으세요?, 날짜: 2023-02-14 18:46:56.209215+00:00>
>>> data={'question_text': '제목수정'}
>>> serializer = QuestionSerializer(new_question, data=data) 
>>> serializer.is_valid()  
True
>>> serializer.save() # Update
<Question: 제목: 제목수정[시리얼라이저에서 업데이트], 날짜: 2023-04-25 13:15:05.852404+00:00>

#Validation이 통과하지 않는 경우
>>> long_text = "abcd"*300
>>> data = {'question_text':long_text}
>>> serializer = QuestionSerializer(data=data)
>>> serializer.is_valid()
False
>>> serializer.errors
{'question_text': [ErrorDetail(string='Ensure this field has no more than 200 characters.', code='max_length')]}

ModelSerializer

polls_api/serializers.py

from rest_framework import serializers
from polls.models import Question

class QuestionSerializer(serializers.ModelSerializer):
    class Meta:
        model = Question
        fields = ['id','question_text', 'pub_date']

Django Shell

>>> from polls_api.serializers import QuestionSerializer
>>> print(QuestionSerializer())
QuestionSerializer():
    id = IntegerField(read_only=True)
    question_text = CharField(max_length=200)
    pub_date = DateTimeField(read_only=True)
>>> serializer = QuestionSerializer(data={'question_text':'모델시리얼라이저로 만들어 봅니다.'})
>>> serializer.is_valid()
True
>>> serializer.save()
<Question: 제목: 모델시리얼라이저로 만들어 봅니다., 날짜: 2023-02-14 19:41:081444+00:00>

Get

정보를 서버에서 획득할 때 사용

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()
def question_list(request):
    questions = Question.objects.all()
    serializer = QuestionSerializer(questions, many = True)
    return Response(serializer.data)

polls_api/urls.py

from django.urls import path
from .views import *

urlpatterns = [
    path('question/', question_list, name='question-list')
]

mysite/urls.py

from django.urls import include, path
from django.contrib import admin

urlpatterns = [
    path('admin/', admin.site.urls),
    path('polls/', include('polls.urls')),
    path('rest/', include('polls_api.urls')),
]

HTTP Methods

HTTP는 요청 메서드를 정의하여, 주어진 리소스에 수행하길 원하는 행동을 나타냅니다. 간혹 요청 메서드를 "HTTP 동사"라고 부르기도 합니다. 각각의 메서드는 서로 다른 의미를 구현하지만, 일부 기능은 메서드 집합 간에 서로 공유하기도 합니다. 이를테면 응답 메서드는 안전하거나, 캐시 가능하거나, 멱등성을 가질 수 있습니다.

데이터 생성(Create) : POST
데이터 조회(Read) : GET
데이터 업데이트(Update) : PUT
데이터 삭제(Delete) : DELETE

POST

polls_api/views.py

from rest_framework.decorators import api_view
from polls.models import Question
from polls_api.serializers import QuestionSerializer
from rest_framework.response import Response
from rest_framework import status

@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)

HTTP Status Code 복습

200번대: 정상적인 결과 - 200 OK - 201 CREATED
400번대: 사용자의 잘못된 요청 - 400 BAD REQUEST - 404 NOT FOUND
500번대: 서버 내부의 오류

PUT / DELETE

views.py

from django.shortcuts import get_object_or_404

@api_view(['GET', 'PUT', 'DELETE'])
def question_detail(request, id):
    question = get_object_or_404(Question, pk=id)
    
    if request.method == 'GET':
        serializer = QuestionSerializer(question)
        return Response(serializer.data)

    if request.method == 'PUT':
        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)

    if request.method == 'DELETE':
        question.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)

polls_api/urls.py

from django.urls import path
from .views import *

urlpatterns = [
    path('question/', question_list, name='question-list'),
    path('question/<int:id>/', question_detail, name='question-detail')
]

Class 기반의 뷰 (Views)

코드가 잘 정리되고 반복되는 코드를 줄일 수 있음

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)

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)

polls_api/urls.py

from django.urls import path
from .views import *

urlpatterns = [
    path('question/', QuestionList.as_view(), name='question-list'),
    path('question/<int:id>/', QuestionDetail.as_view(), name='question-detail'),
]

Mixin

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)

polls_api/urls.py

from django.urls import path
from .views import *

urlpatterns = [
    path('question/', QuestionList.as_view(), name='question-list'),
    path('question/<int:pk>/', QuestionDetail.as_view(), name='question-detail'),
]

Generic API View

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
profile
새싹 개발자

0개의 댓글