요모조모 ʚɞ

[D.R.F] Mixins로 CRUD 구현하기 본문

개발/Django RESTful API

[D.R.F] Mixins로 CRUD 구현하기

Angela_OH 2021. 1. 18. 22:29

 

안녕하세요 :>

오늘은 지난 시간의 APIView로 CRUD 구현하기에 이어, Mixins을 사용해보도록 하겠습니다.

 

APIView와 Mixins의 가장 큰 차이점은 불필요한 코드의 중복을 얼마나 줄일 수 있는가입니다.

APIView로 CRUD를 구현해보면 비슷한 논리의 view가 계속해서 반복된다는 것을 알 수 있습니다.

우리는 CBV(Class Based View)의 상속이라는 특성을 활용하여 view를 좀 더 간단하게 구현해보겠습니다!

Mixins에 대한 자료는 아래의 공식 문서를 참고하였습니다.

www.django-rest-framework.org/tutorial/3-class-based-views/#using-mixins

 

3 - Class based views - Django REST framework

We can also write our API views using class-based views, rather than function based views. As we'll see this is a powerful pattern that allows us to reuse common functionality, and helps us keep our code DRY. We'll start by rewriting the root view as a cla

www.django-rest-framework.org

 

그럼 지난 시간에 작성했던 것과 동일한 순서로 CRUD를 구현해보도록 하겠습니다.

 

models.py

모델은 지난 시간에 사용했던 Blog 모델과 동일한 모델을 사용하도록 하겠습니다.

from django.db import models
from django.conf import settings

class Blog(models.Model):
    title = models.CharField(max_length=100)
    body = models.TextField()

 

serializers.py

serailizer 역시 지난번과 동일한 BlogSerializer를 사용하였습니다.

from .models import Blog
from rest_framework import serializers

class BlogSerializer(serializers.ModelSerializer):
    class Meta:
        model = Blog
        fields = '__all__'

 

views.py

이번 시간에는 Mixins으로 CRUD view를 작성해보았습니다.

from .models import Blog
from .serializers import BlogSerializer
from rest_framework import generics
from rest_framework import mixins

# Blog의 목록을 보여주는 역할
class BlogList(mixins.ListModelMixin, 
                  mixins.CreateModelMixin, 
                  generics.GenericAPIView):
    queryset = Blog.objects.all()
    serializer_class =BlogSerializer

	# Blog list를 보여줄 때
    def get(self, request, *args, **kwargs):
        return self.list(request, *args, **kwargs)
	
	# 새로운 Blog 글을 작성할 때
    def post(self, request, *args, **kwargs):
        return self.create(request, *args, **kwargs)

# Blog의 detail을 보여주는 역할
class BlogDetail(mixins.RetrieveModelMixin,
                     mixins.UpdateModelMixin,
                     mixins.DestroyModelMixin,
                     generics.GenericAPIView):
    queryset = Blog.objects.all()
    serializer_class = BlogSerializer

	# Blog의 detail 보기
    def get(self, request, *args, **kwargs):
        return self.retrieve(request, *args, **kwargs)

	# Blog 수정하기
    def put(self, request, *args, **kwargs):
        return self.update(request, *args, **kwargs)

	# Blog 삭제하기
    def delete(self, request, *args, **kwargs):
        return self.destroy(request, *args, **kwargs)

확실히 APIView로 작성했을 때보다 코드가 간결해짐을 알 수 있습니다!

이렇게 간결해질 수 있었던 이유는 무엇일까요?

그 이유는 바로 앞서 설명드렸던 CBV의 상속 때문입니다.

 

BlogListBlogDetail라는 클래스를 살펴보면 인자로 mixins.ListModelMixin, mixins.CreateModelMixin 등을 받는 것을 알 수 있습니다.

이러한 인자 값은 우리가 views.py 상단에 import 해준 mixins에서 상속을 받은 것입니다.

mixins.py의 원본 코드는 django-rest-framework의 공식 github에서 코드에서 확인해볼 수 있습니다.

github.com/encode/django-rest-framework/blob/master/rest_framework/mixins.py

 

encode/django-rest-framework

Web APIs for Django. 🎸. Contribute to encode/django-rest-framework development by creating an account on GitHub.

github.com

 

mixins.py의 일부

github 코드를 참고하면 다음과 같이 ListModelMixinCreateModelMixin이 정의되어 있는 것을 알 수 있습니다.

이 외에도 RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin이 이미 정의되어 있습니다.

해당 class는 이름의 의미에서도 알 수 있듯이 list를 보여주고, 생성하고, detail을 가져오고, 수정하고, 삭제하는 역할을 한다는 것을 알 수 있습니다.

 

그럼 BlogListBlogDetail에서 선언해준 querysetserializer_class는 어디에서 온 값일까요?

이 값 역시 인자로 상속받은 generics.GenericAPIView에서 찾아볼 수 있습니다.

GenericAPIView는 views.py 상단에서 상속받은 generics에 이미 선언되어 있는 클래스입니다!

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

 

encode/django-rest-framework

Web APIs for Django. 🎸. Contribute to encode/django-rest-framework development by creating an account on GitHub.

github.com

 

generics.py의 일부

generics.py 내부를 살펴보면 querysetserializer_class라는 값이 None으로 설정되어 있는 것을 알 수 있습니다.

우리는 해당 클래스를 상속받은 후 queryset과 serializer_class에 필요한 값을 채워준다고 생각하면 됩니다!

 

이처럼 우리는 rest_framework의 다양한 클래스를 상속받아 view를 좀 더 간단하게 구현할 수 있었습니다.

 

urls.py

마지막으로 urls.py를 생성하여 줍니다.

urls.py도 지난 시간과 동일하게 작성하였습니다.

아래의 코드는 project app 폴더 내부에 urls.py라는 파일을 별도로 생성한 후 작성하였습니다.

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

urlpatterns =[
    path('blog/', views.BlogList.as_view()),
    path('blog/<int:pk>/', views.BlogDetail.as_view()),
]

urlpatterns = format_suffix_patterns(urlpatterns)

 

이후 프로젝트 폴더 내부의 urls.py에 아래의 내용을 작성해주었습니다.

from django.contrib import admin
from django.urls import path, include
from project import urls

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('project.urls')),
]

 

다음과 같이 코드의 작성을 마쳤다면 웹 서버를 동작시켜줍니다!

'127.0.0.1:8000/blog'라는 url로 접속하면 아래의 사진과 같이 글을 등록할 수 있습니다.

127.0.0.1:8000/blog의 POST

 

해당 serializer 양식을 통해 글을 작성하면 아래와 같이 Blog list를 확인할 수 있습니다.

127.0.0.1:8000/blog의 GET

 

마지막으로 '127.0.0.1:8000/blog/<int:pk>'로 접속하게 되면, (위의 예시의 경우에는 '127.0.0.1:8000/blog/2'로 접속)

아래의 사진과 같이 blog 객체의 detail을 보고, 삭제하고, 수정할 수 있습니다.

127.0.0.1:8000/blog/<int:pk>의 GET, PUT, DELETE

 

Mixins을 사용하게 되면 APIView를 사용했을 때보다는 더욱 간단하게 CRUD를 구현할 수 있습니다!

하지만 여전히 반복되는 코드가 많다는 생각이 들 것입니다. (ex. def get()을 두 번이나 작성하다니,,)

다음 시간에는 Generic CBV를 사용하여 주어진 CRUD 코드를 더욱 줄여보도록 하겠습니다!

 

 

Comments