[4/10] TIL - Django를 이용한 API 만들기 [3]

Sangwon Jwa·2024년 4월 10일

데브코스 TIL

목록 보기
13/54
post-thumbnail

📖 학습 주제


  1. Serializer
  2. HTTP 요청 메서드와 상태 코드
  3. API 구현
  • Method 기반 구현
  • Class 기반 구현
  • Mixin을 이용한 구현
  • Generics를 이용한 구현

✏️ 주요 메모 사항 소개


Serialize

모델 인스턴스QuerySet과 같은 데이터를 JSON 형식의 파일로 변환하는 작업. 반대의 작업은 Deserialize라고 한다.
API 통신상에서는 JSON 형식으로 데이터를 주고받는 경우가 많기 때문에 변환 작업이 필요하다.

  1. rest_framework 설치
pip install djangorestframework
  1. /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):
        ## validated_data 기반으로 Question 생성
        return Question.objects.create(**validated_data)
    
    def update(self, instance, validated_data):
        ## instance 기반으로 Question 수정
        instance.question_text = validated_data.get('question_text', instance.question_text)
        instance.save()
        return instance

Django Shell에서 Serializer 사용하기

  1. 관련 라이브러리 import
from polls.models import *
from polls_api.serializers import *
from rest_framework.renderers import JSONRenderer
import json
  1. Serialize
# Question 모델 첫 번째 값 추출 후 QuestionSerializer에 담기
q = Question.objects.first()
serializer = QuestionSerializer(q)
serializer.data
# serialize
json_str = JSONRenderer().render(serializer.data)
json_str

  1. Deserialize
data = json.loads(json_str)
data

ModelSerializer

기존에 작성했던 Serializer를 조금 더 간단하게 변경

# /polls_api/serializers.py

from rest_framework import serializers
from rest_framework.renderers import JSONRenderer
from polls.models import Question

# ModelSerializer 이용시 Meta 정보만 작성하면 create, update 등의 메서드 자동 적용
class QuestionSerializer(serializers.ModelSerializer):
    class Meta:
        model = Question
        fields = ('id', 'question_text', 'pub_date')

HTTP 요청 메서드와 상태코드

 
HTTP 요청 Method

GET : 특정 리소스의 표시를 요청 (read)
POST : 특정 리소스에 새로운 데이터를 생성 요청 (create)
PUT : 특정 리소스에 수정 요청 (update)
DELETE : 특정 리소스에 삭제 요청 (delete)

 

HTTP Status Code (상태코드)

200번대 : 정상적인 결과

  • 200 : OK
  • 201 : CREATED

400번대 : 사용자의 잘못된 요청

  • 400 : BAD REQUEST
  • 404 : NOT FOUND

500번대 : 서버 내부의 오류


API 기능 구현

[GET]

  1. /polls_api/views.py 작성
from django.shortcuts import render
from rest_framework.decorators import api_view
from polls.models import Question
from polls_api.serializers import QuestionSerializer
from rest_framework.response import Response
# Create your views here.

# JSON 형태의 요청과 응답
@api_view()
def question_list(request):
    questions = Question.objects.all()
    # JSON으로 변환
    serializer = QuestionSerializer(questions, many = True)
    return Response(serializer.data)
  1. URL 설정하기
# /mysite/urls.py

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

urlpatterns = [
    path('polls/', include('polls.urls')),
    path('admin/', admin.site.urls),
    # rest 경로 추가 후 polls_api의 url과 연결
    path('rest/', include('polls_api.urls')),
]
# /polls_api/urls.py

from django.urls import path
from .views import *

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

[POST]

  • /polls_api/views.py의 question_list 함수에 POST를 처리하는 부분 작성
from django.shortcuts import render
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
# Create your views here.

# GET과 POST 메서드를 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)
	
    #POST 요청 처리
    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)

[PUT / DELETE]

  • Question 한개에 대한 처리를 하기위해 question_detail 이라는 api를 만들어야 한다. 그 후 필요한 GET, PUT과 같은 메소드를 작성하면 된다.
# polls_api/views.py 에 함수 추가

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

Class 기반의 뷰(Views)

위의 방식은 메서드를 이용해서 api를 구현했지만 class를 기반으로도 구현할 수 있다.
request를 받으면 QuestionList 클래스의 인스턴스를 만든 뒤 request의 요청이 무엇인지에 따라서 get을 호출할 지 post를 호출할 지를 결정

1. QuestionList를 Class 형식으로 변경

# CLASS 기반의 API View
class QuestionList(APIView):

    def get(self, requust):
        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)
        else:
            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)

2. urls.py 변경

from django.urls import path
from .views import *

urlpatterns = [
	# 중간에 메소드가 아닌 클래스가 들어오므로 [클래스명.as_view()] 로 변경
    path('question/', QuestionList.as_view(), name='question-list'),
    path('question/<int:id>/', QuestionDetail.as_view(), name='question-detail')
]

Mixin, Generic API View

class 기반의 api로 변경하면서 코드가 더 간결해졌지만, Mixin 과 Generic View를 이용하면 더욱 더 간결하고 가독성 좋게 코드를 작성할 수 있다.

Mixin 사용

1. Class 변경

# CLASS 기반의 API View / Mixin, GenericAPIView 사용
class QuestionList(mixins.ListModelMixin, mixins.CreateModeMixin, generics.GenericAPIView):
    queryset = Question.objects.all()
    serializer_class = QuestionSerializer
    
    def get(self, requust, *args, **kwargs):
        return self.list(requust, *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, requust, *args, **kwargs):
        return self.retrieve(requust, *args, **kwargs)
    
    def put(self, requust, *args, **kwargs):
        return self.update(requust, *args, **kwargs)
    
    def delete(self, requust, *args, **kwargs):
        return self.destroy(requust, *args, **kwargs)    

2. URL 변경

from django.urls import path
from .views import *

urlpatterns = [
    path('question/', QuestionList.as_view(), name='question-list'),
    # id를 받는 곳에 id가 아닌 pk로 변경해야 됨. Generic APi View는 pk를 이용해서 찾아오기 때문
    path('question/<int:pk>/', QuestionDetail.as_view(), name='question-detail')
]

Generic API View 사용

generics를 사용하면 get,post 메서드 등을 따로 구현하지 않아도 되기 때문에 코드를 말도안되게 더 간단하게 작성이 가능하다.

# CLASS 기반의 API View / Mixin, GenericAPIView 사용
class QuestionList(generics.ListCreateAPIView):
    queryset = Question.objects.all()
    serializer_class = QuestionSerializer

class QuestionDetail(generics.RetrieveUpdateDestroyAPIView):
    queryset = Question.objects.all()
    serializer_class = QuestionSerializer

💦 공부하며 어려웠던 내용


원래 JAVA 언어와 Spring 을 공부하다 와서그런지 View 라는 것이 사용자의 화면을 제어하는 역할이라는 생각이 계속 들어서 헷갈리는 부분이 많았다. Python Django 에서의 View는 비즈니스 로직까지 포함할 수 있다는 것을 처음에는 좀 이질감이 들었는데 강의를 듣다보니 자연스럽게 받아들이게 된 것 같다.

0개의 댓글