오늘도 개발
Django Rest Framework 튜토리얼 따라하기 3. authentications and permissions 본문
Django Rest Framework 튜토리얼 따라하기 3. authentications and permissions
Sueeeeeee 2023. 1. 8. 15:42인증된 유저가 아니면 읽기만 가능하도록 만들기
models.py
장고 기본 User 모델 사용.
Snippet 모델 수정하기(User와 Snippet은 일대다 관계. Snippet이 User를 owner 필드로 참조.)
from django.db import models
from pygments.lexers import get_all_lexers
from pygments.styles import get_all_styles
from pygments.lexers import get_lexer_by_name
from pygments.formatters.html import HtmlFormatter
from pygments import highlight
LEXERS = [item for item in get_all_lexers() if item[1]]
LANGUAGE_CHOICES = sorted([(item[1][0], item[0]) for item in LEXERS])
STYLE_CHOICES = sorted([(item, item) for item in get_all_styles()])
class Snippet(models.Model):
# owner 필드 추가
owner = models.ForeignKey('auth.User', related_name='snippets', on_delete=models.CASCADE)
highlighted = models.TextField()
created = models.DateTimeField(auto_now_add=True)
title = models.CharField(max_length=100, blank=True, default='')
code = models.TextField()
linenos = models.BooleanField(default=False)
language = models.CharField(choices=LANGUAGE_CHOICES, default='python', max_length=100)
style = models.CharField(choices=STYLE_CHOICES, default='friendly', max_length=100)
class Meta:
ordering = ['created']
def save(self, *args, **kwargs):
# django의 save 메서드를 override
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, lineos=linenos, full=True, **options)
self.highlighted = highlight(self.code, lexer, formatter)
# django의 기존 save 메서드 실행
super().save(*args, **kwargs)
serializers.py
1. UserSerializer 추가.
User 모델은 Snippet 모델을 역참조하므로, User 모델에는 명시적으로 snippet 필드를 작성하지 않았다.
그래서 serializer를 만들 때 fields에 바로 snippet 필드를 추가하면 오류가 난다.
모델에 명시적으로 작성하지 않은 역참조 필드는 Meta 클래스 선언 전 추가로 작성해주어야 한다.
2. SnippetSerializer 수정
owner 필드 추가.
from rest_framework import serializers
from snippets.models import Snippet, LANGUAGE_CHOICES, STYLE_CHOICES
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']
class SnippetSerializer(serializers.ModelSerializer):
# source에 넣어준 속성으로 필드를 채움
# User 모델은 바꾸지 않을 것이기 때문에 Read Only 필드로 가져옴.
owner = serializers.ReadOnlyField(source='owner.username')
# owner = serializers.CharField(read_only=True, source='owner.username')과 동일
class Meta:
model = Snippet
fields = ['id', 'owner', 'title', 'code', 'linenos', 'language', 'style']
permissions.py
GET, POST 메서드에만 인증 과정 추가하려면 생략 가능.
PUT, DELETE에도 인증 과정 추가하려면 새 클래스에 has_object_permissions를 정의해주어야 함.
- has_permissons : 디폴트로 가장 먼저 작동하는 메서드.
유저가 로그인했고, 요청 메서드가 SAFE_METHOD에 있는지 확인 => 둘다 통과하면 True 반환
- has_object_permissions : has_permission() 결과가 True일 때만 실행됨. 재정의 후 GET, PUT, DELETE에 사용 가능
from rest_framework import permissions
class IsOwnerOrReadOnly(permissions.BasePermission):
def has_object_permissions(self, request, view, obj):
# permissions.SAFE_METHODS에는 'GET', 'HEAD', 'OPTIONS'가 디폴트로 들어있음.
# GET 요청은 누구나 할 수 있음
if request.method in permissions.SAFE_METHODS:
return True
return obj.owner == request.user
views.py
1. UserList, UserDetail 클래스 추가
2. SnippetList와 SnippetDetail에 perform_create 메서드 작성
=> SnippetSerializer에 유저 정보 넣을 수 있게 만든다.
3. SnippetList에 permission_classes 추가
=> IsAuthenticatedOrReadOnly : 인증된 유저만 생성, 삭제, 수정할 수 있다. 인증되지 않은 유저는 읽기만 가능하게 해준다.
4. SnippetDetail에 permission_classes 추가
=> IsAuthenticatedOrReadOnly(DRF가 제공하는 has_permissons 메서드 사용하는 클래스)
=> IsOwnerOrReadOnly(작성자가 has_object_permissions 메서드를 재정의한 클래스)
from rest_framework import mixins
from rest_framework import generics
from snippets.models import Snippet
from django.contrib.auth.models import User
from snippets.serializers import SnippetSerializer, UserSerializer
class UserList(generics.ListAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
# list 메서드만 있는 ListAPIView를 상속받았고,
# UserList로 들어오는 모든 요청(어차피 get 요청밖에 없음)에 list를 실행하면 되기 때문에
# 따로 get 메서드 작성 안해도 됨
class UserDetail(generics.RetrieveAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
# retrieve 메서드만 있는 RetrieveAPIView를 상속받았고,
# UserDetail로 들어오는 모든 요청(어차피 get 요청밖에 없음)에 retrieve를 실행하면 되기 때문에
# 따로 get 메서드 작성 안해도 됨
class SnippetList(mixins.ListModelMixin,
mixins.CreateModelMixin,
generics.GenericAPIView):
permission_classes = [permissions.IsAuthenticatedOrReadOnly]
queryset = Snippet.objects.all()
serializer_class = SnippetSerializer
# SnippetSerializer의 owner 필드에 현재 유저 오브젝트를 전달
def perform_create(self, serializer):
serializer.save(owner=self.request.user)
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 SnippetDetail(mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
generics.GenericAPIView):
queryset = Snippet.objects.all()
serializer_class = SnippetSerializer
permission_classes = [permissions.IsAuthenticatedOrReadOnly, IsOwnerOrReadOnly]
# SnippetSerializer에 현재 유저 오브젝트를 같이 전달
def perform_create(self, serializer):
serializer.save(owner=self.request.user)
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)
tutorials/urls.py
브라우저로 접속했을 때 로그인 창 뜨게 만드는 설정
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('', include('snippets.urls'))
]
urlpatterns += [
path('login/', include('rest_framework.urls')),
]'웹 프로그래밍 > Django' 카테고리의 다른 글
| django에 데이터베이스 여러 개 연결하기 (0) | 2023.02.18 |
|---|---|
| Django Rest Framework 튜토리얼 따라하기 4. Hyperlink API (0) | 2023.01.08 |
| Django Rest Framework 튜토리얼 따라하기 2. view 쉽게 작성하기 (0) | 2023.01.08 |
| Django Rest Framework 튜토리얼 따라하기 1. Serialization (6) | 2023.01.07 |
| Django ORM 최적화 - select_related, prefetch_related (0) | 2022.08.05 |