FrontEnd
Javascript
Diary
ML
CS
Django
Algorithm
AWS
Co-Work
HTML
CSS
Python
React
ReactNative

Django RESTful Framework(1)

REST란?

  • REpresentational State Transfer 의 약자로 HTTP를 이용해 통신하는 네트워크 상에서 정한 약속

(월드 와이드 웹과 같은 분산 하이퍼미디어 시스템을 위한 소프트웨어 아키텍처의 한 형식)

Representatioional : 자원을 대표하는 식별자로 State Transfer (자원의 상태)를 전송하는 방법

REST

즉 기존에 HTTP method를 이용해서 통신을 해주고, REST는 기존의 하위호환을 깨지않고 네트워크 생태계가 독립적으로 발전할수 있도록 해준다.

REST의 조건에 대해서는 위의 Wiki를 참고하도록 하자

REST API요약

[Network] REST란? REST API란? RESTful이란? - Heee’s Development Blog

그리고 그 이외에 REST에 대한 고찰 논문을 통한 REST에 대한 고찰

REST API는 이대로 괜찮은가? https://www.youtube.com/watch?v=RP_f5dMoHFc&t=829s

API란?

API(Application Programming Interface, 응용 프로그램 프로그래밍 인터페이스)

응용 프로그램에서 사용할 수 있도록, 운영 체제나 프로그래밍 언어가 제공하는 기능을 제어할 수 있게 만든 인터페이스를 뜻한다(인터페이스는 우리가 접하는 프로그램이라 생각)

웹 API

웹 애플리케이션 개발에서 다른 서비스에 요청을 보내고 응답을 받기 위해 정의된 명세를 일컫는다.

예를 들어 블로그 API를 이용하면 블로그에 접속하지 않고도 다른 방법으로 글을 올릴 수 있다.

여기서는 우리가 Request와 Response로 오가는 구조화된 Data를 말한다 비유를 해보면 Client가 식당손님, API가 홀직원, Server가 요리사가 된다 그리고 이러한 형식을 기록하고 나타낸게 API document라고 한다

따라서 위의 두 내용을 정리해보면 HTTP로 CRUD 등을 구현할 수 있는 API를 REST API라 한다

하지만 최근에는 JSON을 통해 정보를 주고받기떄문에 REST 가이드라인이 잘 지켜지지 않는다

왜냐면 JSON이라는 것이 각각의 의미하는것이 다르기 때문에 어떤 것이 무엇을 의미하는지

정해져있지않아 애당초 REST의 목적이었던 독립성이 깨어지고 있는 것이다 (content type 및 Header에 추가적으로 우리가 작성을 해주면 되지만 해주지 않는다)

예) REST의 Uniform interface를 가질조건중 self-descriptive messages : 메세지는 그 하나만으로 모든것이 설명되어야한다 가 잘 지켜지지않음 HATEOAS도 하이퍼미디어를 통해 전달… 잘 지켜지지 않음

Restful API in Django

우선 들어가기에 앞서 JSON 직렬화를 Serializer라고 말합니다 그리고 기존에 우리가 Django와 정보를 주고받았던 Form과 비교해서 보도록 합시다

Django DRF
Form/ModelForm Serializer / ModelSerializer
Model로 부터 Field 읽어옴 Model로 부터 Field 읽어옴
유효성 검사 유효성 검사
HTMLForm JSON 문자열

위의 표를 보면 기존의 Form 과 Serializer가 비슷하게 동작한다는 것을 알수 있습니다.

Data를 Serializer해서 넘겨주면 form 과 같이 is_valid를 이용해서 검사하고 처리해주게 됩니다.

Django REST framework 시작하기

pip install djangorestframework
pip install django
python manage.py startapp <App_name>
INSTALLED_APP 에 우리가 만든 'App_name''rest_framework' 를 등록해주자

Model Serializer 생성 & Model 생성

from django.db import models

# Create your models here.
class Post(models.Model):
    title = models.TextField(max_length = 100)
    body = models.TextField()
from .models import Post
from rest_framework import serializers

class PostSerializer(serializers.ModelSerializer):

    class Meta:
        model = Post
        fields = ('title','body','id',) 
        # fields = '__all__'
        # read_only_fields = ('title',) 이렇게 작성해주면 title field가 오직 읽기만 가능해진다

그리고 언제나 그랬던것 처럼 makemigrations 와 migrate를 해줍시다

그리고 이제부터 4가지의 루트있습니다.

  • API View
  • Mixin
  • Generic CBV
  • ViewSet

위로갈수록 자유도도 높고 어려운 편이며 아래로 갈수록 편하게 구현할수 있는 대신 자유도가 덜한면이 있습니다.

API VIEW

# 데이터 처리 대상
from post.models import Post
from post.serializer import PostSerializer
# status에 따라 직접 Response를 처리할 것
from django.http import Http404 # Get Object or 404 직접 구현

from rest_framework.response import Response
from rest_framework import status
# APIView를 상속받은 CBV
from rest_framework.views import APIView
# PostDetail 클래스의 get_object 메소드 대신 이거 써도 된다
# from django.shortcuts import get_object_or_404

class PostList(APIView):
    def get(self, request):
        posts = Post.objects.all()
        serializer = PostSerializer(posts, many=True) # 쿼리셋 넘기기 (many=True인자)
        return Response(serializer.data) # 직접 Response 리턴해주기 : serializer.data
    
    def post(self, request):
        serializer = PostSerializer(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)
	#Response(data = None , status = None, template name...._)의 인자들이들어갈수있다

# PostList 클래스와는 달리 pk값을 받음 (메소드에 pk인자)
class PostDetail(APIView):  #각 instance 객체는 url뒤에 auto created 된 pk, id값을 붙여줌으로서 우리가 detail을 확인해 줄 수 있다.
    # get_object_or_404를 구현해주는 helper function
    def get_object(self, pk):
        try:
            return Post.objects.get(pk=pk)
        except Post.DoesNotExist:
            raise Http404

    def get(self, request, pk, format=None):
        post = self.get_object(pk)
        # post = get_object_or_404(Post, pk)
        serializer = PostSerializer(post)
        return Response(serializer.data)

    # 위 post 메소드와 비슷비슷한 논리
    def put(self, request, pk, format=None):
        post = self.get_object(pk)
        serializer = PostSerializer(post, 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, format=None):
        post = self.get_object(pk)
        post.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)

urls.py

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

# Default Router 사용 X ==> API ROOT 없음. 

urlpatterns = [
    # 127.0.0.1:8000/post == ListView
    path('post/', views.PostList.as_view()),  
    # 127.0.0.1:8000/post/<pk> == DetailView
    path('post/<int:pk>/', views.PostDetail.as_view()), 
]

#
urlpatterns = format_suffix_patterns(urlpatterns)

여기서 잠깐! 내가 만약 get 이나 post같은 method외에도 put 이나 patch , delete와 같은 method를 만들 수 있는데 이때 put과 patch 의 개념이 헷갈릴 수도 있으니 짚고 넘어가자

  • PUT : 자원의 전체 교체 → 따라서 모든 필드 필요
  • PATCH : 자원의 일부 교체 → 따라서 필드의 일부영역 필요

Mixin

Mixin 이란? : 쉽게 생각하면 다중상속 등에 이용되는 Base Class 를 Mixin Class 라고 한다

주의 사항

class MyClass(ThirdClass, SecondClass, FirstClass)
	pass 

FirstClass → ThirdClass 순서로 상위→ 하위클래스로 작성해줘야 한다는 것이다

# 데이터 처리 대상 : 모델, Serializer import 시키기
from post.models import Post
from post.serializer import PostSerializer

from rest_framework import generics
from rest_framework import mixins
# mixin 직접 보기 : https://github.com/encode/django-rest-framework/blob/master/rest_framework/mixins.py
# genericAPIView 직접 보기 : https://github.com/encode/django-rest-framework/blob/master/rest_framework/generics.py
class PostList(mixins.ListModelMixin, mixins.CreateModelMixin, 
                generics.GenericAPIView):
    queryset = Post.objects.all()   # 쿼리셋 등록!
    serializer_class = PostSerializer # Serializer 클래스 등록!

    # get은 list메소드를 내보내는 메소드
    def get(self, request, *args, **kwargs):
        return self.list(request, *args, **kwargs)

    # post는 create을 내보내는 메소드
    def post(self, request, *args, **kwargs):
        return self.create(request, *args, **kwargs)

class PostDetail(mixins.RetrieveModelMixin, mixins.UpdateModelMixin, 
                mixins.DestroyModelMixin, generics.GenericAPIView):
    queryset = Post.objects.all()
    serializer_class = PostSerializer

    # DetailView의 get은 retrieve을 내보내는 메소드
    def get(self, request, *args, **kwargs):
        return self.retrieve(request, *args, **kwargs)

    # put은 update을 내보내는 메소드
    def put(self, request, *args, **kwargs):
        return self.update(request, *args, **kwargs)

    # delete은 destroy를 내보내는 메소드
    def delete(self, request, *args, **kwargs):
        return self.destroy(request, *args, **kwargs)

urls.py

API View와 동일

Generic CBV

from post.models import Post
from post.serializer import PostSerializer
from rest_framework import generics

# rest_framework/generics.py
# https://github.com/encode/django-rest-framework/blob/master/rest_framework/generics.py

# ListCreateAPIView
# https://github.com/encode/django-rest-framework/blob/0e1c5d313232a131bb4a1a414abf921744ab40e0/rest_framework/generics.py#L232

# RetrieveUpdateDestroyAPIView
# https://github.com/encode/django-rest-framework/blob/0e1c5d313232a131bb4a1a414abf921744ab40e0/rest_framework/generics.py#L274

class PostList(generics.ListCreateAPIView):
    queryset = Post.objects.all()
    serializer_class = PostSerializer

class PostDetail(generics.RetrieveUpdateDestroyAPIView):
    queryset = Post.objects.all()
    serializer_class = PostSerializer

urls.py

API View와 동일

ViewSets & Routers

Decorator란 무엇인가요

def hello_fun(message):
    # 안녕함수가 인사할수 있는 내부로직 구현
    def greeting(name):
        print(f'{message} - {name}!')
    return greeting

    my_hello_fun= hello_fun('Hello')

    my_hello_fun('chichi') # Hello - chichi!
    my_hello_fun('jang') # Hello - jang!

위의 코드와 같은게 Decorator라고 느껴보자

ViewSet

from post.models import Post
from post.serializer import PostSerializer

from rest_framework import viewsets

# @action처리
from rest_framework import renderers
from rest_framework.decorators import action
from django.http import HttpResponse

'''
# ReadOnlyModelViewSet은 말 그대로 ListView, DetailView의 조회만 가능
class PostViewSet(viewsets.ReadOnlyModelViewSet):
    queryset = Post.objects.all()
    serializer_class = PostSerializer
'''
# ModelViewSet은 ListView와 DetailView에 대한 CRUD가 모두 가능

class PostViewSet(viewsets.ModelViewSet):
    queryset = Post.objects.all()
    serializer_class = PostSerializer

    # @action(method=['post']) #커스텀 method
    @action(detail=True, renderer_classes=[renderers.StaticHTMLRenderer])
		#rerender는  Response를 어떤방식으로 Rendering시킬것인가에 대한 이야기다
		#default는  JSON이랑 BrowsableAPIrenderer를 많이 쓴다
    # 그냥 얍을 띄우는 custom api
    def highlight(self, request, *args, **kwargs):
        return HttpResponse("얍")

API서버를 담당하는 백엔드는 HTML관여에 최소화하는게 좋다 customAPI의 default Method는 기본 GET방식이다

ViewSet Router

from rest_framework.routers import DefaultRouter
from django.urls import include, path
from post import views

# 라우터가 없다면?
router = DefaultRouter()
router.register('post', views.PostViewSet)

urlpatterns = [
    path('', include(router.urls))
]

as_view()안에 http_method가 오고 그에 해당하는 함수가 와서 원래는 맵핑된다 그런데 우리는 router라는 것을 이용하면 이런것들을 자동으로 register해줄수 있따