[엉박사] 1.4 CRUD API

impala·2023년 1월 12일
0
post-thumbnail

1.4 CRUD API

DRF를 통해 API를 구현하는 기본적인 순서는 다음과 같다.

Model 작성 -> View 작성 -> Serialiser 작성 -> url등록

또한 장고에는 두 종류의 View가 있는데, 하나는 함수형 뷰(FBV: Function Based View) 이고, 다른 하나는 클래스형 뷰(CBV : Class Based View) 이다.

FBV는 @api_view Decorator을 활용하여 HTTP 메소드를 표시하지만,
CBV는 APIView를 상속받아 HTTP 메소드(get, post, put, delete)를 구현하는 방식으로 API를 작성한다.

CRUD란 Create, Read, Update, Delete의 줄임말로, 데이터베이스를 활용한 서비스가 가지는 기본적인 기능의 집합이다. 장고에서는 여러가지 방법으로 CRUD를 구현할 수 있다.

1.4.1 API VIEW

DRF에서 미리 구현해 둔 APIView클래스를 상속받아 메소드를 구현한다.

"""
기본적인 APIView 구조
"""
class Class_name(APIView):
    def method_name(self, request, format=None):
        # 구체적인 동작은 개발자가 구현
        # method_name : get, post, put, delete중 하나
        serializer = serializer_name()
        return Response()

APIView는 API를 작성하기 위한 기본적인 틀을 제공해주기 때문에 자유도가 높지만 그만큼 반복되는 작업이 많고, 코드가 길어진다.

class SnippetList(APIView):
    # List : 전체 데이터 읽어오기
    def get(self, request, format=None):
        snippets = Snippet.objects.all()
        serializer = SnippetSerializer(snippets, many=True)
        return Response(serializer.data)

    # Create : 데이터 생성
    def post(self, request, format=None):
        serializer = SnippetSerializer(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 SnippetDetail(APIView):
    def get_object(self, pk):
        try:
            return Snippet.objects.get(pk=pk)
        except Snippet.DoesNotExist:
            raise Http404

    # Retrive(Read) : 데어터 1개 읽어오기
    def get(self, request, pk, format=None):
        snippet = self.get_object(pk)
        serializer = SnippetSerializer(snippet)
        return Response(serializer.data)

    # Update : 데이터 수정
    def put(self, request, pk, format=None):
        snippet = self.get_object(pk)
        serializer = SnippetSerializer(snippet, data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    # Delete : 데이터 삭제
    def delete(self, request, pk, format=None):
        snippet = self.get_object(pk)
        snippet.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)

1.4.2 Mixins

APIView를 통해 CRUD를 구현한 코드를 보면 하나의 클래스 내에서 모델에서 가져온 데이터나 시리얼라이저가 중복되는 것을 볼 수 있다.

DRF에서는 이러한 중복들을 제거하기 위해 클래스 수준에서 시리얼라이저를 등록하여 Mixin으로 구현해 두었다. Mixin의 종류는 다음과 같다.

  • ListModelMixin : 전체 데이터 조회 (return self.list())
  • CreateModelMixin : 데이터 1개 생성 (return self.create())
  • RetriveModelMixin : 데이터 1개 조회 (return self.retrive())
  • UpdateModelMixin : 데이터 1개 수정 (return self.update())
  • DestroyModelMixin : 데이터 1개 삭제 (return self.destroy())
"""
Minin 구조
"""
class Class_name(Mixnin, GenericAPIView):
    queryset = # 모델에서 가져온 데이터
    serializer_class = # 시리얼라이저 클래스 명
    # queryset과 serializer_class는 GenericAPIView에서 상속

    def method_name(self, request, *args, **kwargs):
        # method_name : get, post, put, delete중 하나
        return self.method(request, *args, **kwargs)
        # method : list, create, retrive, update, destroy

1.4.1의 코드를 Mixin을 사용하면 아래와 같이 바꿀 수 있다.

class SnippetList(mixins.ListModelMixin,
                  mixins.CreateModelMixin,
                  generics.GenericAPIView):
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer

    # List
    def get(self, request, *args, **kwargs):
        return self.list(request, *args, **kwargs)

    # Create
    def post(self, request, *args, **kwargs):
        return self.create(request, *args, **kwargs)

class SnippetDetail(mixins.RetrieveModelMixin,
                    mixins.UpdateModelMixin,
                    mixins.DestroyModelMixin,
                    generics.GenericAPIView):
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer

    # Read
    def get(self, request, *args, **kwargs):
        return self.retrieve(request, *args, **kwargs)

    # Update
    def put(self, request, *args, **kwargs):
        return self.update(request, *args, **kwargs)

    # Delete
    def delete(self, request, *args, **kwargs):
        return self.destroy(request, *args, **kwargs)

1.4.3 Generic View

Mixin을 통해 CRUD를 구현한 경우 APIView를 상속한 것에 비해 중복이 많이 사라지긴 했지만 각 기능마다 다른 클래스를 상속받아야 하기 때문에 상속받는 클래스가 많아 번거롭다.

그래서 DRF에서는 자주 쓰이는 Mixin들을 조합하여 GenericView로 묶어두어 상속의 번거로움을 줄였다. DRF의 GenericView의 종류는 다음과 같다.

  • genericsListAPIView : List
  • genericsCreateAPIView : Create
  • genericsRetriveAPIView : Retrive
  • genericsUpdateAPIView : Update
  • genericsDestroyAPIView : Destroy
  • genericsListCreateAPIView : List + Create
  • genericsRetriveUpdateAPIView : Retrive + Update
  • genericsRetriveDestroyAPIView : Retrive + Destroy
  • genericsRetriveUpdateDestoryAPIView : Retrive + Update + Destroy
"""
generics 구조
"""
class Class_name(GenericAPIView):
    queryset = # 모델에서 가져온 데이터
    serializer_class = # 시리얼라이저 클래스 명

Generic View를 활용하면 쿼리셋과 시리얼라이저만 지정하면 별도의 메소드 구현 없이 상속받은 generics클래스에서 지원하는 기능들을 사용할 수 있다.

1.4.2의 코드를 generics를 사용하면 아래와 같이 줄일 수 있다.

# List + Create
class SnippetList(generics.ListCreateAPIView):
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer

# Retrive + Update + Delete
class SnippetDetail(generics.RetrieveUpdateDestroyAPIView):
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer

1.4.4 Viewset

위의 View들을 하나의 클래스에서 하나의 URL을 담당하는 방식이었기 때문에 여러 클래스에서 queryset이나 serializer_class가 겹치는 경우가 생겼다.

그래서 DRF에서는 Viewset을 통해 하나의 클래스에서 하나의 모델에 대한 CRUD API를 모두 다룰 수 있도록 묶어두었다. 이로 인해 코드가 겹치는 부분을 최소화하고, 라우터를 통해 일정한 규칙의 URL을 만들 수 있게 하였다.

"""
Viewset 구조
"""
# views.py
class Class_name(Viewsets):
    queryset = # 모델에서 가져온 데이터
    serializer_class = # 시리얼라이저 클래스 명

# urls.py
router = DefaultRouter()
router.register('URL', Class_name)
urlpatterns = [
    path('', include(routers.urls))
]

1.4.3의 코드를 Viewset을 활용하면 아래와 같이 작성할 수 있다.

class SnippetViewSet(viewsets.ModelViewSet):
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer

지금까지 APIView, Mixins, generics, Viewset을 통해 CRUD기능을 구현하였다. 뒤로 갈수록 중복이 제거되어 코드가 짧아지고 기능들이 많아지지만 개발자의 자유도가 낮아져 커스터마이징이나 수정이 불편해진다.

0개의 댓글