본문 바로가기
Django

DRF Tutorial 4 Authentication & Permissions

by AlbertIm 2024. 9. 14.

시작

이번 포스트에서는 DRF(Django Rest Framework) 튜토리얼을 따라 AuthenticationPermissions을 다루는 방법을 알아보겠습니다. 본 포스트는 macOSVS Code 환경에서 진행됩니다.

본문

Snippet 모델과 유저 모델 관계 추가

먼저 snippets/models.pySnippet 모델에 새로운 필드를 추가하여 사용자와 연결합니다.

owner = models.ForeignKey('auth.User', related_name='snippets', on_delete=models.CASCADE)
highlighted = models.TextField()

 

또한 pygments 라이브러리를 사용하여 코드 조각의 HTML 표현을 강조 표시할 수 있는 save 메서드를 정의합니다.

# ...
from pygments.lexers import get_lexer_by_name
from pygments.formatters.html import HtmlFormatter
from pygments import highlight

# ...

class Snippet(models.Model):
    # ...
    def save(self, *args, **kwargs):
        """
        `pygments` 라이브러리를 사용하여 강조 표시된 HTML 만들기 코드 조각의 표현.
        """
        lexer = get_lexer_by_name(self.language)
        linenos = 'table' if self.linenos else False
        options = {'title': self.title} if self.title else {}
        formatter = HtmlFormatter(style=self.style, linenos=linenos,
                                  full=True, **options)
        self.highlighted = highlight(self.code, lexer, formatter)
        super().save(*args, **kwargs)

 

이제 데이터베이스 테이블을 업데이트해야 합니다. 아래 명령어를 실행하여 변경 사항을 반영합니다.

rm -f db.sqlite3
rm -r snippets/migrations
python manage.py makemigrations snippets
python manage.py migrate

 

마지막으로 superuser를 생성하여 관리 권한을 가진 사용자를 추가합니다.

python manage.py createsuperuser

유저 모델에 대한 endpoints를 추가

snippets/serializers.py에 유저를 위한 serializer를 추가합니다.

from django.contrib.auth.models import User
# ...

# ...

class UserSerializer(serializers.ModelSerializer):
    snippets = serializers.PrimaryKeyRelatedField(many=True, queryset=Snippet.objects.all())

    class Meta:
        model = User
        fields = ['id', 'username', 'snippets']

 

여기서 snippets은 유저 모델의 reverse 관계이기 때문에 ModelSerializer 클래스를 사용할 때 기본적으로 포함되지 않으므로 이에 대한 명시적 필드로 추가해야 합니다.

 

그리고 generic class-based viewsListAPIViewRetrieveAPIView를 사용하여 snippets/views.py에 유저 뷰를 추가합니다.

from django.contrib.auth.models import User
from snippets.serializers import UserSerializer
# ...

# ...

class UserList(generics.ListAPIView):
    queryset = User.objects.all()
    serializer_class = UserSerializer


class UserDetail(generics.RetrieveAPIView):
    queryset = User.objects.all()
    serializer_class = UserSerializer

 

마지막으로 snippets/urls.py에 유저 뷰의 URL 패턴을 추가합니다.

# ...

urlpatterns = [
    # ...
    path('users/', views.UserList.as_view()),
    path('users/<int:pk>/', views.UserDetail.as_view()),
]

# ...

Snippet과 유저 연결

snippet이 생성될 때 해당 유저 정보가 함께 저장되도록 SnippetList 뷰의 perform_create() 메서드를 재정의합니다.

snippets/views.pySnippetList view를 수정합니다.

def perform_create(self, serializer):
    serializer.save(owner=self.request.user)

이제 Snippet을 생성할 때 자동으로 현재 요청을 보낸 유저가 owner 필드로 저장됩니다.

Serializer 업데이트

serializers.pySnippetSerializer를 업데이트하여 유저를 반영합니다.

class SnippetSerializer(serializers.ModelSerializer):
    owner = serializers.ReadOnlyField(source="owner.username")

    class Meta:
        model = Snippet
        fields = ["id", "title", "code", "linenos", "language", "style", "owner"]

여기 ReadOnlyFiled는 직렬화할 때는 활성화되지만 역직렬화할 때는 비활성화됩니다.

권한 설정 추가

SnippetListSnippetDetail 뷰에 permissions.IsAuthenticatedOrReadOnly를 추가하여 인증된 사용자만 스니펫을 생성, 수정, 삭제할 수 있게 하고 인증되지 않은 사용자에게는 읽기 권한만 부여합니다.

from django.contrib.auth.models import User
from rest_framework import generics
from rest_framework import permissions

from snippets.models import Snippet
from snippets.serializers import SnippetSerializer, UserSerializer


class SnippetList(generics.ListCreateAPIView):
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer
    permission_classes = [permissions.IsAuthenticatedOrReadOnly]

    def perform_create(self, serializer):
        serializer.save(owner=self.request.user)


class SnippetDetail(generics.RetrieveUpdateDestroyAPIView):
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer
    permission_classes = [permissions.IsAuthenticatedOrReadOnly]


# ...

로그인 기능 추가

config/urls.pyapi-auth 패턴을 추가하여 브라우저에서 로그인 기능을 활성화합니다.

urlpatterns += [
    path('api-auth/', include('rest_framework.urls')),
]

 

API를 사용할 때 로그인하지 않은 상태에서는 로그인 버튼이 제공됩니다.

 

로그인을 클릭하면 로그인 페이지로 이동하게 됩니다.

Object 기준 권한 설정

snippet의 소유자만 수정할 수 있도록 권한을 설정하려고 permissions.py 파일을 생성하고 아래 코드를 추가합니다.

from rest_framework import permissions


class IsOwnerOrReadOnly(permissions.BasePermission):
    """
    소유자만 수정할 수 있는 권한을 부여합니다.
    """

    def has_object_permission(self, request, view, obj):
        # 읽기 권한은 모든 요청에 허용됩니다.
        # 따라서 GET, HEAD 또는 OPTIONS 요청은 항상 허용됩니다.
        if request.method in permissions.SAFE_METHODS:
            return True

        # 쓰기 권한은 스니펫의 소유자에게만 허용됩니다.
        return obj.owner == request.user

 

 

SnippetDetail 뷰에서 이 권한을 적용합니다.

from snippets.permissions import IsOwnerOrReadOnly

# ...

class SnippetDetail(generics.RetrieveUpdateDestroyAPIView):
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer
    permission_classes = [permissions.IsAuthenticatedOrReadOnly,
                          IsOwnerOrReadOnly]

# ...

마무리

이 포스트에서는 Django Rest Framework를 사용하여 인증과 권한 설정을 추가하는 방법을 살펴보았습니다. 이를 통해 DRF에서 인증과 권한을 설정하는 기본적인 방법을 이해할 수 있었습니다.

참고자료

'Django' 카테고리의 다른 글

DRF Tutorial 6 ViewSets & Routers  (0) 2024.09.14
DRF Tutorial 5 Relationships & Hyperlinked APIs  (0) 2024.09.14
DRF Tutorial 3 Class-based Views  (0) 2024.09.14
DRF Tutorial 2 Requests and Responses  (0) 2024.09.11
DRF Tutorial 1 Serialization  (0) 2024.09.11

댓글