[DRF] View

GreenBeanยท2021๋…„ 9์›” 27์ผ
0
post-thumbnail

View

  • Django์—์„œ๋Š” View๋ฅผ ํ†ตํ•ด์„œ HTTP ์š”์ฒญ์„ ์ฒ˜๋ฆฌ
  • View์—์„œ ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ๋‹ค์–‘ํ•จ
    • CBV(ํด๋ž˜์Šค ๊ธฐ๋ฐ˜ ๋ทฐ), FBV(ํ•จ์ˆ˜ ๊ธฐ๋ฐ˜ ๋ทฐ)๋ฅผ ํ†ตํ•ด์„œ๋„ API๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ์ง€๋งŒ REST framework์—์„œ๋Š” ๋ณด๋‹ค ์‰ฝ๊ณ  ํšจ์œจ์ ์œผ๋กœ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Œ

APIView ํด๋ž˜์Šค์™€ api_view ์žฅ์‹์ž

  • APIView์™€ api_view๋Š” ๊ฐ๊ฐ CBV์™€ FBV์— ๋Œ€์‘๋˜๋Š” ๋‚ด์šฉ
  • ๋‘ ๊ฐ€์ง€ ๋ชจ๋‘ View์— ์—ฌ๋Ÿฌ ๊ฐ€์ง€ ๊ธฐ๋ณธ ์„ค์ •์„ ๋ถ€์—ฌํ•˜๊ฒŒ ๋˜๋Š”๋ฐ ์ƒํ™ฉ์— ๋งž์ถฐ์„œ ์ปค์ŠคํŠฌํ•˜์—ฌ ์‚ฌ์šฉ
  • ์ง๋ ฌํ™” ํด๋ž˜์Šค ์ง€์ •
    • renderer_classes
    • default
      • JSON ์ง๋ ฌํ™” : rest_framework.renderers.JSONRenderer
      • HTML ํŽ˜์ด์ง€ ์ง๋ ฌํ™” : rest_framework.renderers.TemplateHTMLRenderer
  • ๋น„์ง๋ ฌํ™” ํด๋ž˜์Šค ์ง€์ •
    • parser_classes
    • default
      • JSON ํฌ๋งท ์ฒ˜๋ฆฌ : rest_framework.parsers.JSONParser
      • FormParser : rest_framework.parsers.FormParser
      • MultiPartParser : rest_framework.parsers.MultiPartParser
  • ์ธ์ฆ ํด๋ž˜์Šค ์ง€์ •
    • authentication_classes
    • default
      • ์„ธ์…˜๊ธฐ๋ฐ˜์ธ์ฆ : rest_framework.authentication.SessionAuthentication
      • HTTP basic ์ธ์ฆ : rest_framework.authentication.BasicAuthentication
  • ์‚ฌ์šฉ๋Ÿ‰ ์ œํ•œ ํด๋ž˜์Šค ์ง€์ •
    • throttle_classes
    • default
      • ๋นˆ ํŠœํ”Œ
  • ๊ถŒํ•œ ํด๋ž˜์Šค ์ง€์ •
    • permission_classes
    • default
      • ๋ˆ„๊ตฌ๋ผ๋„ ์ ‘๊ทผ ํ—ˆ์šฉ : rest_framework.permissions.AllowAny
  • ์š”์ฒญ์— ๋”ฐ๋ผ ์ ์ ˆํ•œ ์ง๋ ฌํ™”/๋น„์ง๋ ฌํ™” ์„ ํƒ
    • content_negotiation_class
    • ๊ฐ™์€ URL ์š”์ฒญ์— ๋Œ€ํ•ด์„œ JSON ์‘๋‹ต์„ ํ•  ์ง€, HTML ์‘๋‹ต์„ ํ•  ์ง€ ํŒ๋‹จ
    • default
      • rest_framework.negotiation.DefaultContentNegotiation
  • ์š”์ฒญ ๋‚ด์—ญ์—์„œ API ๋ฒ„์ „ ์ •๋ณด๋ฅผ ํƒ์ง€ํ•  ํด๋ž˜์Šค ์ง€์ •
    • versioning_class
    • ์š”์ฒญ URL์˜ HEADER์—์„œ ๋ฒ„์ „ ์ •๋ณด๋ฅผ ํƒ์ง€ํ•˜์—ฌ ๋งž๋Š” ๋ฒ„์ „์„ ํ˜ธ์ถœ
    • default
      • ๋ฒ„์ „ ์ •๋ณด๋ฅผ ํƒ์ง€ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. : None

APIView

  • CBV ์ค‘ ํ•˜๋‚˜์ด๊ธฐ ๋•Œ๋ฌธ์— ํ•˜๋‚˜์˜ URL์— ๋Œ€ํ•ด์„œ๋งŒ ์ฒ˜๋ฆฌ๋ฅผ ํ•  ์ˆ˜ ์žˆ์Œ
    • /article/์— ๋Œ€ํ•œ CBV
      • get : article ๋ชฉ๋ก
      • post : ์ƒˆ article ์ƒ์„ฑ
    • /article/<int:pk>/์— ๋Œ€ํ•œ CBV
      • get : pk๋ฒˆ article ๋‚ด์šฉ
      • put : pk๋ฒˆ article ์ˆ˜์ •
      • delete : pk๋ฒˆ article ์‚ญ์ œ
  • ์š”์ฒญ method์— ๋งž๊ฒŒ ๋ฉค๋ฒ„ ํ•จ์ˆ˜๋ฅผ ์ •์˜ํ•˜๋ฉด ํ•ด๋‹น method ๋กœ request๊ฐ€ ๋“ค์–ด์˜ฌ ๋•Œ ํ˜ธ์ถœํ•˜๊ฒŒ ๋จ
def get(self, request): 
    pass 

def post(self, request): 
    pass 
    
def put(self, request): 
    pass 

def delete(self, request): 
    pass

Tip! ๋ฉค๋ฒ„ ํ•จ์ˆ˜
ํด๋ž˜์Šค์˜ ๋‚ด๋ถ€์—์„œ ์ •์˜๋œ ํ•จ์ˆ˜๋ฅผ ๋ฉค๋ฒ„ ํ•จ์ˆ˜(member function)๋ผ๊ณ  ํ•˜๋ฉฐ, ๋•Œ๋กœ๋Š” ๋ฉ”์†Œ๋“œ(method)๋ผ๊ณ  ๋ถˆ๋ฆฌ๊ธฐ๋„ ํ•จ

  • ๊ฐ method๊ฐ€ ํ˜ธ์ถœ๋˜๋ฉด ์œ„์—์„œ ๋ดค๋˜ ์„ค์ •์— ๋งž์ถฐ ์ฒ˜๋ฆฌ๊ฐ€ ์ด๋ฃจ์–ด์ง
    • ์ง๋ ฌํ™”/๋น„์ง๋ ฌํ™”
    • ์ธ์ฆ ํ™•์ธ
    • ์‚ฌ์šฉ๋Ÿ‰ ์ œํ•œ ํ™•์ธ
    • ๊ถŒํ•œ ํ™•์ธ
    • ์š”์ฒญํ•œ ๋ฒ„์ „ ํ™•์ธ
# views.py

class ArticleListCreateAPIView(APIView):

    def get(self, request):
        articles = Article.objects.filter(active=True)
        serializer = ArticleSerializer(articles, many=True)
        return Response(serializer.data)      

    def post(self, request):
        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)


class ArticleDetailAPIView(APIView):

    def get_object(self, pk):
        article = get_object_or_404(Article, pk=pk)
        return article

    def get(self, request, pk):
        article = self.get_object(pk)
        serializer = ArticleSerializer(article)
        return Response(serializer.data)

    def put(self, request, pk):
        article = self.get_object(pk)
        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)

    def delete(self, request, pk):
        article = self.get_object(pk)
        article.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)
def get_object(self, pk):
    article = get_object_or_404(Article, pk=pk)
    return article
# urls.py

urlpatterns = [
    path("articles/", 
         ArticleListCreateAPIView.as_view(), 
         name="article-list"),

    path("articles/<int:pk>/", 
         ArticleDetailAPIView.as_view(), 
         name="article-detail"),
 ]
  • ๊ฐ ํด๋ž˜์Šค์— ๋Œ€ํ•ด .as_view()๋กœ ๋ผ์šฐํŒ…์„ ํ•ด์ฃผ๋ฉด View์—์„œ ์ด๋“ค์„ ๋ถˆ๋Ÿฌ์™€ ์š”์ฒญ์„ ์ฒ˜๋ฆฌ๋ฅผ ํ•ด์ฃผ๊ฒŒ ๋จ

@api_view

  • api_view๋Š” FBV์— ๋Œ€ํ•ด์„œ ์‚ฌ์šฉํ•˜๋Š” ์žฅ์‹์ž
# views.py

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

@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)
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)
# urls.py

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

Mixin

  • APIView๋Š” ๊ฐ request method ๋งˆ๋‹ค ์ง์ ‘ serializer ์ฒ˜๋ฆฌ๋ฅผ ํ•ด์ฃผ์–ด์•ผ ํ•˜๋Š”๋ฐ ์ด ๊ณผ์ •์—์„œ ์—ฌ๋Ÿฌ serializer์— ๋Œ€ํ•ด ์ค‘๋ณต์ด ๋ฐœ์ƒํ•˜๊ฒŒ ๋จ
  • rest_framework.mixins์—์„œ๋Š” ์ด๋Ÿฌํ•œ ๊ธฐ๋Šฅ๋“ค์ด ๋ฏธ๋ฆฌ ๊ตฌํ˜„์ด ๋˜์–ด ์žˆ์Œ
    • CreateModelMixin
    • ListModelMixin
    • RetrieveModelMixin
    • UpdateModelMixin
    • DestroyModelMixin
  • queryset๊ณผ serializer_class๋ฅผ ์ง€์ •ํ•ด์ฃผ๊ธฐ๋งŒ ํ•˜๋ฉด ๋‚˜๋จธ์ง€๋Š” ์ƒ์† ๋ฐ›์€ Mixin๊ณผ ์—ฐ๊ฒฐ๋จ
# views.py

class EbookListCreateAPIView(mixins.ListModelMixin,
                             mixins.CreateModelMixin,
                             generics.GenericAPIView):

    queryset = Ebook.objects.all()
    serializer_class = EbookSerializer

    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 EbookDetailMixins(mixins.RetrieveModelMixin,
                        mixins.UpdateModelMixin,
                        mixins.DestroyModelMixin,
                        generics.GenericAPIView):
                        
    queryset = Ebook.objects.all()
    serializer_class = EbookSerializer

    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.delete(request, *args, **kwargs)
# urls.py

urlpatterns = [
    path("ebooks/", 
         EbookListCreateAPIView.as_view(), 
         name="ebook-list"),

    path("ebooks/<int:pk>/", 
         EbookDetailAPIView.as_view(), 
         name="ebook-detail"),
]

Tip! GenericAPIView
GenericAPIView์—์„œ๋Š” lookup_field='pk'๋ฅผ default๋กœ ๊ฐ–๊ณ  ์žˆ์Œ


Concrete View Classes

  • Mixin์„ ์ƒ์†ํ•จ์œผ๋กœ์„œ ๋ฐ˜๋ณต๋˜๋Š” ๋‚ด์šฉ์„ ๋งŽ์ด ์ค„์ผ ์ˆ˜ ์žˆ์Œ
  • ํ•˜์ง€๋งŒ ์—ฌ๋Ÿฌ ๊ฐœ๋ฅผ ์ƒ์†ํ•ด์•ผ ํ•˜๋‹ค๋ณด๋‹ˆ ๊ฐ€๋…์„ฑ์ด ๋–จ์–ด์ง
  • REST Framework์—์„œ๋Š” Mixin์„ ์ƒ์†ํ•œ ์ƒˆ๋กœ์šด ํด๋ž˜์Šค๊ฐ€ ์ •์˜๋˜์–ด ์žˆ์Œ
  • ์ด 9๊ฐœ์˜ ํด๋ž˜์Šค
    • generics.CreateAPIView : ์ƒ์„ฑ
    • generics.ListAPIView : ๋ชฉ๋ก
    • generics.RetrieveAPIView : ์กฐํšŒ
    • generics.DestroyAPIView : ์‚ญ์ œ
    • generics.UpdateAPIView : ์ˆ˜์ •
    • generics.RetrieveUpdateAPIView : ์กฐํšŒ/์ˆ˜์ •
    • generics.RetrieveDestroyAPIView : ์กฐํšŒ/์‚ญ์ œ
    • generics.ListCreateAPIView : ๋ชฉ๋ก/์ƒ์„ฑ
    • generics.RetrieveUpdateDestroyAPIView : ์กฐํšŒ/์ˆ˜์ •/์‚ญ์ œ
# views.py

class EbookListCreateAPIView(generics.ListCreateAPIView):
    queryset = Ebook.objects.all().order_by("id")
    serializer_class = EbookSerializer


class EbookDetailAPIView(generics.RetrieveUpdateDestroyAPIView):
    queryset = Ebook.objects.all()
    serializer_class = EbookSerializer
# urls.py

urlpatterns = [
    path("ebooks/", 
         EbookListCreateAPIView.as_view(), 
         name="ebook-list"),

    path("ebooks/<int:pk>/", 
         EbookDetailAPIView.as_view(), 
         name="ebook-detail"),
]

ViewSet

  • generics.APIView๋ฅผ ํ†ตํ•ด์„œ ์ฝ”๋“œ๋ฅผ ๋งŽ์ด ๊ฐ„์†Œํ™” ํ•˜์˜€์ง€๋งŒ ์•„์ง queryset๊ณผ serializer_class๊ฐ€ ๊ณตํ†ต์ ์ธ๋ฐ๋„ ๋ถˆ๊ตฌํ•˜๊ณ  ๋”ฐ๋กœ ๊ธฐ์žฌํ•ด์ฃผ์–ด์•ผํ–ˆ์Œ
  • ์ด๋ฅผ ํ•œ ๋ฒˆ์— ์ฒ˜๋ฆฌํ•ด์ฃผ๋Š”๊ฒŒ ๋ฐ”๋กœ ViewSet
  • ViewSet์€ CBV๊ฐ€ ์•„๋‹Œ ํ—ฌํผ ํด๋ž˜์Šค๋กœ ๋‘ ๊ฐ€์ง€ ์ข…๋ฅ˜๊ฐ€ ์žˆ์Œ
    • viewsets.ReadOnlyModelViewSet : ๋ชฉ๋ก ์กฐํšŒ, ํŠน์ • ๋ ˆ์ฝ”๋“œ ์กฐํšŒ
    • viewsets.ModelViewSet : ๋ชฉ๋ก ์กฐํšŒ, ํŠน์ • ๋ ˆ์ฝ”๋“œ ์ƒ์„ฑ/์กฐํšŒ/์ˆ˜์ •/์‚ญ์ œ
  • ViewSet์˜ ๋‘ ์ข…๋ฅ˜ ๋ชจ๋‘ ์ž์ฒด์ ์ธ ๊ตฌํ˜„์€ ์—†๊ณ  Mixin์„ ์ ์ ˆํžˆ ์ƒ์† ๋ฐ›์•„ ์—ฌ๋Ÿฌ ๊ธฐ๋Šฅ์„ ์ˆ˜ํ–‰
    • viewsets.ReadOnlyModelViewSet
      • ๋ชฉ๋ก ์กฐํšŒ
        • mixins.ListModelMixin : list() ํ•จ์ˆ˜
      • ํŠน์ • ๋ ˆ์ฝ”๋“œ ์กฐํšŒ
        • mixins.RetrieveModelMixin : retrieve() ํ•จ์ˆ˜
    • viewsets.ModelViewSet
      • ๋ชฉ๋ก ์กฐํšŒ
        • mixins.ListModelMixin : list() ํ•จ์ˆ˜
      • ํŠน์ • ๋ ˆ์ฝ”๋“œ ์กฐํšŒ
        • mixins.RetrieveModelMixin : retrieve() ํ•จ์ˆ˜
      • ํŠน์ • ๋ ˆ์ฝ”๋“œ ์ƒ์„ฑ
        • mixins.CreateModelMixin : create() ํ•จ์ˆ˜
      • ํŠน์ • ๋ ˆ์ฝ”๋“œ ์ˆ˜์ •
        • mixins.UpdateModelMixin : update() ํ•จ์ˆ˜, partial_update() ํ•จ์ˆ˜
        • partial_update(): ๋ถ€๋ถ„์ ์ธ ํ•„๋“œ ๊ฐ’๋งŒ ๋ฐ›์•„ ์ˆ˜์ •์ด ๊ฐ€๋Šฅํ•˜๋ฉฐ request method ์ค‘ Fetch์™€ ๋Œ€์‘๋จ
      • ํŠน์ • ๋ ˆ์ฝ”๋“œ ์‚ญ์ œ
        • mixins.DestroyModelMixin : destroy() ํ•จ์ˆ˜
profile
๐ŸŒฑ Backend-Dev | hwaya2828@gmail.com

0๊ฐœ์˜ ๋Œ“๊ธ€