TIL119. DRF : APIView 간단 사용기

ID짱재·2022년 2월 11일
1

Django REST Framework

목록 보기
3/10
post-thumbnail

📌 이 포스팅에서는 DRF의 APIView를 활용하여 CRUD 기능의 View를 구현해보았습니다.



🌈 APIView 간단 사용기

🔥 APIView로 ListView 만들기

🔥 APIView를 활용하여 CRUD 구현



1. APIView로 ListView 만들기

🤔 models.py & serializers.py

✔️ filter 기능을 사용하기 위해 is_public 필드를 추가하였다.

# models.py
from django.conf import settings
from django.db   import models
class Post(TimeStamp):
    author    = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
    message   = models.TextField()
    is_public = models.BooleanField(default=False, db_index=True)
    created_at = models.DateTimeField(auto_now_add=True) # 👈 추가
    updated_at = models.DateTimeField(auto_now=True)

✔️ 마이그레이션 후, PostSerializer에도 추가된 "is_public" 필드를 fields에 추가한다.

# serializers.py
from rest_framework import serializers
from .models import Post
# ostSerializer
class PostSerializer(serializers.ModelSerializer):
    username = serializers.ReadOnlyField(source='author.username')
    class Meta:
        model = Post
        fields = [
            'pk',
            'username',
            'message',
            'is_public', # 👈 추가
            'created_at',
            'updated_at',
        ]

🤔 CBV로 ListView 구현하기

✔️ 게시글 목록을 제공하는 View를 API를 아래처럼 만들 수 있다. APIView는 기본으로 제공하는 뷰로 View를 이미 상속받았다.

✔️ 참고로, APIView는 csrf_exempt 데코레이션으로 이미 감싸져있기 때문에 POST 요청에서 csrf token 체크를 하지 않는다.

✔️ APIView는 rest_framework에 View에 있고, 응답을 위해 Response를 import하여 사용할 수 있다.

✔️ Serializer는 첫번째 인자로 instance를 주고, 다수(Qeuryset)일 경우 many=True 옵션을 준다.

✔️ serializer로 GET 매서드를 사용할 때는, 첫번째 인자에 instance 또는 Queryset를 주어야 한다.

✔️ serializer한 data에 .data를 붙이면, 직렬화된 데이터를 쉽게 반환받을 수 있다.

from rest_framework.views    import APIView
from rest_framework.response import Response
from .serializers            import PostSerializer
from .models                 import Post
# PublicPostListAPIView
class PublicPostListAPIView(APIView):
    def get(self, request):
        qs = Post.objects.filter(is_public=True)
        serializer = PostSerializer(qs, many=True) # 👈 다수의 객체들을 이용할 때는 many=True 옵션을 지정
        return Response(serializer.data)
public_post_list = PublicPostListAPIView.as_view()        

✔️ urls.py는 아래와 같이 매핑해준다.

from django.urls            import include, path
from rest_framework.routers import DefaultRouter
from .                      import views
router = DefaultRouter()
router.register('post', views.PostViewSet) 
urlpatterns = [
    path('public/', views.public_post_list), # 👈 localhost:8000/public
    path('', include(router.urls)),
]

🤔 FBV로 ListView 구현하기

✔️ FBV로 구현할 때는 framework.decorators에 "api_view"를 import 해준다.

✔️ "api_view"를 함수에 데코레이션으로 달고, 그 안에 HTTP 매서드를 매핑해준다.

from rest_framework.decorators import api_view
from rest_framework.response   import Response
from .serializers              import PostSerializer
from .models                   import Post
# public_post_list
@api_view(['GET']) # 👈 GET 매서드 지정
def public_post_list(request):
    qs = Post.objects.filter(is_public=True)
    serializer = PostSerializer(qs, many=True)
    return Response(serializer.data)

🤔 ListAPIView로 ListView 구현하기

✔️ APIView를 통해 CBV, FBV를 구현했던 것을 generic.ListAPIView를 이용하여 더 손쉽게 구현할 수 있다. 단, generic에서 제공하는 View로 하나의 Class에서 하나의 URL과 매핑된다.

✔️ 즉, APIView는 가장 기본이 되는 View이며, 이를 표준화한 것이 generic에서 제공하는 View이다. 이를 더 표준화하고 통합해서 여러 URL을 하나의 Class로 처리할 수 있는 것이 ViewSet이라고 생각하면 된다.

✔️ ListAPIView는 rest_framework.generics를 import하여 사용한다.

✔️ queryset와 serializer_class를 지정해주면, 위의 기능과 같이 작동한다.

from rest_framework            import generics
from rest_framework.response   import Response
from .serializers              import PostSerializer
from .models                   import Post
# PublicPostListAPIView
class PublicPostListAPIView(generics.ListAPIView):
    queryset = Post.objects.filter(is_public=True)
    serializer_class = PostSerializer
public_post_list = PublicPostListAPIView.as_view()  


2. APIView를 활용하여 CRUD 구현

🤔 PostListView(GET, POST)

✔️ PostListAPIView는 GET 요청으로 공개 게시글에 대한 list를 제공하거나, 게시글을 생성할 수 있는 API이다.

✔️ save() 매서드를 호출할 때, 현재 작성자를 참조할 수 있도록, 인자로 넘겨줘야하는데 이 때 model에서 사용하는 field명을 지정해야한다. ReadOnly로 지정할 필드명을 상용할 경우 Error가 발생한다.

from rest_framework.views      import APIView
from rest_framework.response   import Response
from .serializers              import PostSerializer
from .models                   import Post
class PostListAPIView(APIView):
    # public post list 요청
    def get(self, request):
        qs = Post.objects.filter(is_public=True)
        serializer = PostSerializer(qs, many=True)
        return Response(serializer.data)
    # post 작성
    def post(self, request):
        serializer = PostSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save(author=request.user) # 👈 save 매서드에 인자로 전달
            return Response(serializer.data, status=201)
        return Response(serializer.errors, status=400)
public_post_list = PostListAPIView.as_view()  

🤔 PostDetailAPIView(GET, PUT, DELETE)

✔️ PostDetailAPIView는 pk(id)를 기준으로 읽기, 수정, 삭제 기능을 구현할 수 있다.

✔️ urls.py의 경로는 아래와 같이 pk값을 읽어올 수 있도록 명시한다.

from django.urls import include, path
from rest_framework.routers import DefaultRouter
from . import views
router = DefaultRouter()
router.register('post', views.PostViewSet) 
urlpatterns = [
    path('public/', views.public_post_list),
    path('public/<int:pk>/', views.public_post_detail), # 👈 추가
    path('', include(router.urls)),
]

✔️ 해당 pk에 대한 객체를 가져오기 위해 get_object_or_404를 import 한다.

✔️ 수정은 첫번째 인자로 기존 인스턴스를 넘겨주고, 두번째 인자로 수정할 데이터(request.data)를 전달한다.

from django.shortcuts          import get_object_or_404 # 👈 추가
from rest_framework.views      import APIView
from rest_framework.response   import Response
from .serializers              import PostSerializer
from .models                   import Post
# PostDetailAPIView
class PostDetailAPIView(APIView):
    def get_object(self, pk):
        return get_object_or_404(Post, pk=pk)
    def get(self, request, pk, format=None):
        post = self.get_object(pk)
        serializer = PostSerializer(post)
        return Response(serializer.data)
    def put(self, request, pk):
        post = self.get_object(pk)
        serializer = PostSerializer(post, data=request.data) # 👈 instance, 수정 데이터 순
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
    def delete(self, request, pk):
        post = self.get_objects(pk)
        post.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)
public_post_detail = PostDetailAPIView.as_view()        
profile
Keep Going, Keep Coding!

0개의 댓글