터칭 데이터

장고(Django) 믹스인(Mixin) 본문

장고 (Django)

장고(Django) 믹스인(Mixin)

터칭 데이터 2023. 11. 4. 20:30

 

 

 

 

믹스인이란?

 

 

Django에서 Mixin은 클래스를 구성 요소로 재사용하고 확장하는 데 사용되는 방법 중 하나입니다. Mixin은 기능을 추가하고 확장하는 데 도움이 되며, Django 클래스 기반 뷰, 모델, 폼 및 다른 컴포넌트에서 특히 유용합니다.

Mixin의 주요 특징과 역할은 다음과 같습니다:

1. **재사용성**: Mixin 클래스는 여러 클래스에서 재사용될 수 있습니다. 여러 뷰나 모델에서 동일한 기능을 사용하고자 할 때 유용합니다.

2. **기능 확장**: Mixin은 기존 클래스의 기능을 확장하거나 추가합니다. 예를 들어, Django의 클래스 기반 뷰에서 인증, 캐싱, 페이징, 검색 및 다른 기능을 뷰 클래스에 추가할 수 있습니다.

3. **계층 구조**: Mixin 클래스는 다른 클래스와 함께 다중 상속을 사용하여 조합됩니다. 이것은 다양한 Mixin 클래스를 조합하여 원하는 기능을 갖는 클래스를 생성할 수 있게 해줍니다.

4. **모듈화**: Mixin을 사용하면 코드를 모듈화하고 더 쉽게 관리할 수 있습니다. 관련 기능을 Mixin 클래스에 구현하고, 필요한 클래스에서 해당 Mixin 클래스를 사용하여 이러한 기능을 활성화합니다.

5. **가독성과 유지 보수성**: 코드의 재사용과 모듈화를 통해 가독성과 유지 보수성을 향상시킵니다. Mixin을 사용하면 코드를 논리적으로 구성할 수 있으며, 기능을 추가하거나 변경할 때 해당 Mixin 클래스만 수정하면 됩니다.

예를 들어, Django에서 인증 관련 기능을 Mixin으로 사용하는 경우, 다양한 뷰 클래스에서 로그인, 로그아웃, 권한 확인 등의 기능을 간단하게 추가할 수 있습니다. Mixin 클래스는 주로 `Mixin` 또는 `Mixin`으로 끝나는 이름을 가집니다.

다음은 Django에서 Mixin을 사용하여 인증 기능을 추가하는 간단한 예제입니다:

from django.contrib.auth.decorators import login_required
from django.views.generic import View
from django.utils.decorators import method_decorator

class LoginRequiredMixin:
    @method_decorator(login_required)
    def dispatch(self, *args, **kwargs):
        return super().dispatch(*args, **kwargs)

class MyView(LoginRequiredMixin, View):
    def get(self, request):
        # 이 뷰는 로그인이 필요한 뷰입니다.
        # LoginRequiredMixin Mixin을 사용하여 로그인 검사를 수행합니다.



이 예제에서 `LoginRequiredMixin`은 Mixin 클래스로, 뷰 클래스 `MyView`에 로그인이 필요한 기능을 추가합니다. Mixin 클래스에서 `dispatch` 메서드를 오버라이드하여 로그인 검사를 수행하고, `method_decorator` 데코레이터로 `login_required` 데코레이터를 적용합니다. 이렇게 함으로써 `MyView` 클래스는 로그인이 필요한 뷰로 동작하게 됩니다.

 

 

 

 

 

 

 

 

 

 

 

 

믹스인으로 더 간단하게

 

 

우리가 지난 시간 클래스 기반 뷰(Django Class-Based Views)로 작성한 코드 입니다.

 

 

polls_api/views.py

 

 

from django.shortcuts import render, get_object_or_404
# from rest_framework.decorators import api_view
from polls.models import Question
from .serializers import QuestionSerializer
from rest_framework.response import Response
from rest_framework import status
from rest_framework.views import APIView
# Create your views here.

class QuestionList(APIView):
    def get(self, request):
        questions = Question.objects.all()
        serializer = QuestionSerializer(questions, many = True)
        return Response(serializer.data)
    
    def post(self, request):
        serializer = QuestionSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        else:
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

class QuestionDetail(APIView):
    def get(self, request, id):
        question = get_object_or_404(Question, pk=id)
        serializer = QuestionSerializer(question)
        return Response(serializer.data)
    
    def put(self, request, id):
        question = get_object_or_404(Question, pk=id)
        serializer = QuestionSerializer(question, data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_200_OK)
        else:
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
    
    def delete(self, request, id):
        question = get_object_or_404(Question, pk=id)
        question.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)

 

메서드 기반 뷰에서 상당히 코드양이 줄었는데요

 

여기서 더 줄일 수 있습니다!

 

 

 

 

polls_api/view.py

from django.shortcuts import render, get_object_or_404
# from rest_framework.decorators import api_view
from polls.models import Question
from .serializers import QuestionSerializer
from rest_framework.response import Response
from rest_framework import status, mixins, generics
from rest_framework.views import APIView
# Create your views here.

class QuestionList(mixins.ListModelMixin, mixins.CreateModelMixin, generics.GenericAPIView):
    queryset = Question.objects.all()
    serializer_class = QuestionSerializer
    
    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 QuestionDetail(mixins.RetrieveModelMixin, mixins.UpdateModelMixin, mixins.DestroyModelMixin, generics.GenericAPIView):
    queryset = Question.objects.all()
    serializer_class = QuestionSerializer
    
    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.destroy(request, *args, **kwargs)

 

결과물부터 살펴보니 코드가 더욱 깔끔하고 간단해졌습니다.

 

 

 

 

 

urls.py도 수정

 

 

블록 부분에 기존에는 id가 적혀있었는데 이를 pk로 바꿔줍니다.

왜냐하면 뷰에서 상속받은 generics.GenericAPIView가 pk를 이용해 QuestionDetail 업무를 수행하기 때문입니다.

 

매우 자세한 설명은 여기를 참고해주세요.

더보기

Django REST framework의 generics.APIView를 사용할 때 URL 패턴에서 pk (Primary Key) 값을 사용하는 것은 일반적으로 RESTful API 디자인의 일부입니다. 이렇게 하는 이유는 RESTful API 디자인의 규칙을 따르기 위해서이며, 다음과 같은 이점을 제공합니다:

일관성: RESTful API는 자원(데이터 모델)을 나타내는 URL을 갖고 있습니다. 각 자원은 고유한 식별자(primary key)를 갖고 있으며, 이를 통해 해당 자원을 식별합니다. pk는 이 식별자를 나타내므로 API의 URL에서 일관성을 유지할 수 있습니다.

가독성: URL에서 pk를 사용하면 API 엔드포인트가 어떤 자원을 나타내는지 명확하게 이해하기 쉽습니다. 예를 들어, /api/posts/1/은 "ID가 1인 포스트 자원"을 나타냅니다.

유연성: pk는 일반적으로 정수값이지만, 문자열로도 사용할 수 있습니다. 이렇게 하면 문자열로 식별자를 사용하는 경우도 처리할 수 있습니다.

Django 모델 연동: Django의 데이터 모델은 일반적으로 pk를 기본 식별자로 사용합니다. 따라서 Django REST framework와 Django 모델 간의 연동을 쉽게 할 수 있습니다.

예를 들어, generics.RetrieveAPIView를 사용하여 개별 객체의 조회를 처리할 때, URL에서 pk를 사용하면 해당 객체를 고유하게 식별할 수 있습니다. URL 패턴을 정의할 때 pk 대신 다른 이름을 사용할 수도 있지만, 일반적으로 pk를 사용하여 RESTful한 API를 작성하고 가독성을 높이는 것이 좋습니다.

 

 

 

 

 

 

그런데 mixins와 generics와 거기에 붙어나오는 list(), create(), retrieve(), update(), destroy()까지 처음 접하는 우리에게는 다소 낯설고 당황스러울 겁니다.

 

 

 

 

갑자기 많은 내용이 몰아닥쳐서 그렇지 하나씩 살펴보면 그리 어려운 내용은 아닙니다.

 

먼저 list(), create(), retrieve(), update(), destroy()는 우리가 지금껏 구현해온 CRUD를 각각 상속 받은 mixin들에서 이미 구현되어있는 것을 가져다 쓴다고 이해하시면 좋습니다.

 

 

 

Generics와 Mixins

 

generics는 위에서 이미 mixin을 설명했으므로 둘을 비교하며 설명하겠습니다.

 

Django에서 Generics와 Mixins은 모두 코드 재사용과 기능 확장을 지원하는 데 사용되는 두 가지 다른 접근 방식입니다. 이 둘 간의 주요 차이점은 다음과 같습니다:

1. **Generics**:
   - `generics` 모듈은 Django REST framework에서 기본적인 CRUD(Create, Retrieve, Update, Delete) 작업과 관련된 API 뷰 및 믹스인 클래스를 제공합니다.
   - Generics 클래스는 특정 작업(예: 목록 조회, 개별 객체 조회, 생성, 업데이트, 삭제)을 수행하는 API 뷰를 미리 정의하고 제공합니다.
   - Generics 클래스는 시리얼라이저(Serializer)와 데이터 모델을 자동으로 연결하며, 이러한 클래스를 상속받아 뷰를 작성하면 데이터베이스 모델과 연동된 API 엔드포인트를 쉽게 설정할 수 있습니다.

2. **Mixins**:
   - Mixin 클래스는 Django REST framework에서 기능을 조합하고 확장하기 위한 방법 중 하나입니다.
   - Mixin 클래스는 다른 클래스와 조합하여 사용되며, 특정 기능을 추가하거나 확장할 수 있습니다.
   - Mixin은 재사용 가능한 기능 세트를 나타내며, 뷰 클래스에 필요한 Mixin을 조합하여 뷰 클래스의 동작을 커스터마이즈할 수 있습니다.

일반적으로 Generics 클래스는 일반적인 CRUD 작업과 관련된 API 뷰를 빠르게 설정하기 위해 사용되며, Mixin 클래스는 다양한 기능을 조합하고 커스터마이즈하기 위해 사용됩니다. Generics는 특정 작업에 특화되어 있고, Mixins는 뷰 클래스에 여러 기능을 추가하거나 수정할 때 사용됩니다. 이 둘을 조합하여 Django REST framework를 사용하면 강력한 API를 쉽게 구축할 수 있습니다.

 

 

 

 

지금은 완벽하게 이해하시기 어렵겠지만 괜찮습니다.

 

mixins와 generics의 도움을 받아 장고를 더 쉽고 간단하게 사용할 수 있다는 점을 기억해주세요.

 

 

 

 

 
 
 

'장고 (Django)' 카테고리의 다른 글

장고(Django) 정참조와 역참조  (3) 2023.11.05
장고(Django) Generic API View  (0) 2023.11.04
장고(Django) 클래스(Class) 기반의 뷰(Views)  (0) 2023.11.04
장고(Django) PUT/DELETE  (0) 2023.11.04
장고(Django) POST  (0) 2023.11.04