본문 바로가기

Python/Library

[Django] REST framework tutorial - Requests and Responses

 

* 목차

- Intro

- Code 작성

- URL에 format suffix 추가하기

- Test

 

*이전글

 

Django REST framework tutorial1 - Serialization

* 목차 - intro - Set Environment - Serialization을 위한 Model 작성 - Serializer class 만들기 - Serializer 사용하기 - ModelSerializers 사용하기 - Serializer를 Django View에서 활용하기 - Web API test Intro 이번 시간에는 django

tyoon9781.tistory.com

 


Intro

이전글에서는 단순히 Serializer를 소개하는 수준이었다면 이번 Tutorial에서는 rest framework의 핵심을 본격적으로 다르겠습니다. 몇가지 필수 building block을 소개하겠습니다.

Request 객체

Django에서의 HttpRequest를 확장하는 Request를 지원하여 보다 유연한 요청구문 분석을 제공합니다. Request 객체의 핵심기능은 request.data 속성으로, request.POST보다 웹API 작업에 더 유용합니다.

Response 객체

TemplateResponse과 같은 Type의 Response 객체는 컨텐츠 협상(Contents Negotiation)을 통해 렌더링되지 않은 컨텐츠를 가져와 클라이언트에 어떻게 반환할지 결정합니다.

Status codes

view에 HTTP 상태 숫자를 직접 입력한다면 실수를 했을 때 알아차리기 어렵습니다. rest framework는 각 상태 코드에 대해 명시적인 식별자를 제공합니다. 예를 들면 status 모듈에 있는 HTTP_400_BAD_REQUEST같은 것이 있습니다.

Wrapping API views

rest framework는 api view에 사용할 수 있는 wrapper 2개를 제시합니다.

 

 1. function 기반의 api_view decorator (@api_view)

 2. class 기반의 APIView

 

이 wrapper들은 view에서 request instances를 수신하도록 하고, 컨텐스 협상을 수행할 수 있도록 응답 개체에 context를 추가하는 등 몇 가지 기능을 제공합니다.

 

wrapper는 또한 정의하지 않는 method로 접근할 경우  405 Method Not Allowed를 반환할 수 있으며, 잘못된 입력으로 request.data에 access할 때 ParseError 예외를 처리할 수 있습니다.

 


Code 작성

위에서 언급했던 것들을 사용해서 view를 새로 작성해 보곘습니다. tutorial 1에서 작성했던 코드는 다음과 같습니다.

@csrf_exempt
def snippets(request):
	## 혀용할 method를 명시하지 않았다.
    if request.method == "GET":
        serializer = SnippetSerializer(Snippet.objects.all(), many=True)
        return JsonResponse(serializer.data, safe=False)
    
    elif request.method == "POST":
        data = JSONParser().parse(request)	## ParseError 처리 없음
        serializer = SnippetSerializer(data=data)
        if serializer.is_valid():
            serializer.save()
            return JsonResponse(serializer.data, status=201) 	## 숫자 status 명시
        return JsonResponse(serializer.errors, status=400)	## 숫자 status 명시

 

이 코드를 refactoring하겠습니다.

from rest_framework import status
from rest_framework.decorators import api_view
from rest_framework.response import Response
from .models import *
from .serializers import *


## functino decorator 활용
@api_view(["GET", "POST"])	
def snippets(request):
    if request.method == "GET":
        serializer = SnippetSerializer(Snippet.objects.all(), many=True)
        return Response(serializer.data)

    elif request.method == "POST":
    	## request.data로 parserError handling 가능
        serializer = SnippetSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)	## 명시적 status 활용
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)  ## 명시적 status 활용

 

이런 방식으로 snippet_detail도 다시 작성해 보겠습니다. 마찬가지로 tutorial 1에서부터 이어집니다.

@api_view(["GET", "PUT", "POST"])
def snippet_detail(request, pk):
    try:
        snippet = Snippet.objects.get(pk=pk)
    except Snippet.DoesNotExist:
        return Response(status=status.HTTP_404_NOT_FOUND)
    
    if request.method == "GET":
        serializer = SnippetSerializer(snippet)
        return Response(serializer.data)
    elif request.method == "PUT":
        serializer = SnippetSerializer(snippet, data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        else:
            return Response(serializer.errors, status=status.HTTP_404_NOT_FOUND)
    elif request.method == "DELETE":
        snippet.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)

 

보시면 전체적으로 status를 사용했고, request.data를 통해 data를 parsing하고 있습니다. request data를 json같이 명시적인 format으로 처리하고 있지 않음을 주목하시기 바랍니다. request.data는 json 요청을 처리할 수도 있자만 다른 format도 처리할 수 있습니다. 마찬가지로 response 객체를 반환할 때 rest framework가 올바른 컨텐츠 유형으로 렌더링하도록 허용합니다.

 


URL에 format suffix 추가하기

response가 더이상 한 가지 format에 고정되어 있지 않다는 사실을 활용하기 위해 API endpoint에 format suffix에 대한 지원을 추가합니다. format suffix를 사용하면 특정 형식을 명시적으로 참조하는 URL이 제공되며, 이는 API가 "http://example.com/api/items/4.json"같은  URL도 처리할 수 있음을 의미합니다.

 

views에 format keyword를 추가하겠습니다.

def snippets(request, format=None):

...

def snippet_detail(request, pk, format=None):

...

 

그리고 toy/urls.py에 format_suffex_patterns를 추가하도록 하겠습니다.

from django.urls import path
from rest_framework.urlpatterns import format_suffix_patterns
from . import views

urlpatterns = [
    path('snippets/', views.snippets),
    path('snippets/<int:pk>/', views.snippet_detail),
]

urlpatterns = format_suffix_patterns(urlpatterns)

 

Test

Tutorial 1에서 했던 것처럼 작성된 API를 test 해보겠습니다.

snippets/

 

snippets.json/

format_suffix_patterns를 추가하고, views에 format=None을 설정한 덕분에 json url도 가능합니다.

 

snippets.api/

이 요청은 browsing할 수 있는 url입니다. 

 

format에 정해지지 않은 format을 작성하면 다음과 같이 출력 됩니다.

 

POST request도 한 번 날려보겠습니다. rest framework에서는 Browser에서 POST를 날릴 수 있습니다.

{
    "title": "",
    "code": "print(456)",
    "linenos": false,
    "language": "python",
    "style": "friendly"
}

 

 

그럼 HTTP 201 response를 확인할 수 있습니다.

 

다시 snippets를 확인하면 입력한 data를 조회할 수 있습니다.

 


마치며...

API는 client 요청에 따라 응답의 contents type이 정해지기 때문에 web browser에서는 보통 html 형식을 반환합니다. 따라서 API는 web browser에서 완전히 검색 가능한 html 형식을 반환할 수 있습니다.

 

web browsing이 가능한 api를 사용하면 사용성이 크게 향상되고 api개발과 사용이 훨씬 쉬워집니다. 또한 API를 검사하고 작업하고자 하는 다른 개발자의 진입장벽이 낮아집니다. 

 

다음 tutorial에서는 class기반의 view 작성과 generic view를 사용하면 코드량이 얼마나 줄어드는지 확인해 봅시다.

 

 


*reference

https://www.django-rest-framework.org/tutorial/2-requests-and-responses/

'Python > Library' 카테고리의 다른 글

[Redis] - Tutorial with Python  (4) 2024.09.21
[Alembic] - Tutorial  (7) 2024.09.20
[Django] REST framework tutorial - Serialization  (0) 2023.06.23
[Flask] - Quickstart  (0) 2023.04.17
[Flask] - Tutorial  (0) 2023.04.17