DRF 2 - Function-based view, Request, Response (News api 2편)

Joey Lee·2020년 7월 9일
5

Django REST Framework

목록 보기
3/16
post-custom-banner

앞 장에서는 Article Model과 Serializer를 만들었다.
이제 Article을 Create-Retrieve-Update-Delete하는 뷰를 만들어 보자. DRF에서 뷰를 만드는 것은 Pure Django에서와 마찬가지로 함수형과 클래스형 2가지로 만들 수 있다.

REST프레임워크는 API 뷰를 만드는데 사용할 수 있는 2가지 wrapper를 제공한다.

  • 함수형 뷰를 위한 @api_view
  • 클래스형 뷰를 위한 APIView

wrapper 에서는 뷰에서 Request 인스턴스를 수신하고, 해당 메소드를 인자로 전달해서 해당 메소드에 맞는 로직이 실행되도록 도와준다. 즉, @api_view는 클래스형 뷰의 as_view()처럼 여러 가지 유형의 메소드를 함수형에서도 처리해 줄 수 있도록 도와준다고 보면 될 듯 하다.

1. 엔드포인트 구성 (urls.py)

엔드포인트는 pk 정보가 필요 없는 List, Create를 수행하는 뷰와 pk 정보가 필요한 Detail, Update, Delete를 수행하는 뷰, 2가지로 작성할 수 있다.

[api/urls.py]

from django.urls import path
from news.api.views import (article_list_create_api_view,
                            article_detail_api_view)

urlpatterns = [
    path("articles/", article_list_create_api_view, name='article-list'),
    path("articles/<int:pk>", article_detail_api_view, name='article-detail')
]

2. Article List/Create View 만들기

뉴스 아티클의 리스트를 뿌려주는 기능과 리스트를 추가하는 기능은 하나의 뷰에서 처리할 수 있도록 아래와 같이 코드를 짤 수 있다.

1) views.py 전체 코드

from rest_framework import status
from rest_framework.decorators import api_view
from rest_framework.response import Response

from news.models import Article
from news.api.serializers import ArticleSerializer

@api_view(["GET", "POST"])
def article_list_create_api_view(request):
    if request.method == "GET":
        articles = Article.objects.filter(active=True)
        serializer = ArticleSerializer(articles, many=True)  
        # many => queryset에 대응. many 없으면 instance 1개가 올 것으로 기대하고 있어 에러 발생함.
        return Response(serializer.data)

    elif request.method == "POST":
        serializer = ArticleSerializer(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)

2) Debug를 통한 views.py 처리 로직 이해하기

Article을 생성하는 POST 메소드가 처리되는 로직을 debug로 살펴보면 아래와 같다.
아래 내용을 통해 request.data, request.query_params, serializer에는 각각 어떤 값들이 담겨줘서 처리하게 되는지 이해할 수 있다.

> /Users/seungholee/udemy_drf/newsapi/news/api/views.py(79)article_list_create_api_view()
-> serializer = ArticleSerializer(data=request.data)
(Pdb) n
> /Users/seungholee/udemy_drf/newsapi/news/api/views.py(80)article_list_create_api_view()
-> if serializer.is_valid():
(Pdb) request
<rest_framework.request.Request object at 0x10fa3a150>
(Pdb) request.data
{'time_since_publication': '1 month, 2 weeks', 'author': 'Joey', 'title': 'Trade force', 'description': "it's tough", 'body': 'hahahah', 'location': 'Seoul', 'publication_date': '2020-07-09T08:31:45Z', 'active': True}
(Pdb) request.query_params
<QueryDict: {}>
(Pdb) request.parsers
[<rest_framework.parsers.JSONParser object at 0x10fa148d0>, <rest_framework.parsers.FormParser object at 0x10fa3a450>, <rest_framework.parsers.MultiPartParser object at 0x10fa3a190>]
(Pdb) n
> /Users/seungholee/udemy_drf/newsapi/news/api/views.py(81)article_list_create_api_view()
-> serializer.save()
(Pdb) serializer
ArticleSerializer(data={'time_since_publication': '1 month, 2 weeks', 'author': 'Joey', 'title': 'Trade force', 'description': "it's tough", 'body': 'hahahah', 'location': 'Seoul', 'publication_date': '2020-07-09T08:31:45Z', 'active': True}):
    id = IntegerField(label='ID', read_only=True)
    time_since_publication = SerializerMethodField()
    author = StringRelatedField()
    title = CharField(max_length=120)
    description = CharField(max_length=200)
    body = CharField(style={'base_template': 'textarea.html'})
    location = CharField(max_length=120)
    publication_date = DateTimeField()
    active = BooleanField(required=False)
    created_at = DateTimeField(read_only=True)
    updated_at = DateTimeField(read_only=True)

3) Request, Response 공식문서 살펴보기

3-1) Request

DRF에서는 HTTP 요청 객체로서 HttpRequest 객체를 확장한 Request 객체를 사용한다. Request는 HttpRequest 객체보다 요청 내용을 유연하게 파싱할 수 있도록 돕는다. 자주 사용하게 될 Request의 속성은 아래와 같다.

  • Request Parsing
    • request.data : (POST, PUT, PATCH메소드에서) Body에 담겨 전달된 데이터를 리턴 (key:value)
    • request.query_params : 쿼리스트링으로 전달되는 데이터를 리턴 (key:value)
  • Request Authentication
    • request.user : django.contrib.auth.models.User의 객체를 리턴
    • request.auth : 해당 객체의 token를 리턴 (없다면, None을 리턴)
  • Browser enhancements
    • request.method : request의 메소드를 리턴
    • request.content_type : request의 컨텐트타입을 리턴

[request.POST와 request.data의 차이점]

request.POST  # 폼 데이터만 처리할 수 있고, POST 메소드에서만 동작한다.
request.data  # 임의의 데이터를 처리할 수 있고, POST, PUT, PATCH 메소드에서 동작한다.

2-2) Response

렌더링되지 않은 내용을 읽어서 클라이언트가 요청한 콘텐트 타입에 맞는 형식으로 자동 렌더링 해 준다. Pure Django에서는 전달할 데이터에 따라 HttpResponse, JsonResponse를 개발자가 직접 지정을 해 주어야 하지만, DRF의 Response를 이용하면 알아서 렌더링을 해 주기 때문에 편하게 쓸 수 있다.

# Signature
Response(data, status=None, template_name=None, headers=None, content_type=None)

# use case
return Response(data, status.HTTP_201_CREATED)

2-3) status

REST Framework에서는 status 모듈 안에 각각의 상태정보를 속성으로 담고 있다. 따라서 status.[상태 속성값]을 호출하면 그에 맞는 상태 값이 전달된다.

status.HTTP_200_OK
stauts.HTTP_400_BAD_REQUEST

2. Article Detail View 만들기

뉴스 아티클의 디테일 정보 가져오기, 수정하기, 삭제하기를 수행하는 뷰를 만들어 보자.

1) views.py 전체 코드

@api_view(["GET", "PUT", "DELETE"])
def article_detail_api_view(request, pk):
    try:
        article = Article.objects.get(pk=pk)
    except Article.DoesNotExist:
        return  Response({"error" : {
            "code" : 404,
            "message" : "Article not found"
            }}, status=status.HTTP_404_NOT_FOUND)
    
    if request.method == 'GET':
        serializer = ArticleSerializer(article)
        return Response(serializer.data)

    elif request.method == 'PUT':
        serializer = ArticleSerializer(article, data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
    
    elif request.method == 'DELETE':
        article.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)
profile
안녕하세요!
post-custom-banner

0개의 댓글