[DRF] <Level Two> Django REST Framework - 5. Permission on Review

Alex of the year 2020 & 2021·2020년 9월 2일
0

Django Rest Framework

목록 보기
9/15
post-thumbnail

Permission (2)

  • ebook의 review에 대해, 해당 리뷰를 적은 user에 의해서만 수정/삭제가 가능하게 하는 permission 클래스 구현

Code

1) 먼저, 이 구현을 위해서는 Review Model에 대한 수정이 필요하다.
User 모델을 임포트해야 한다. 우선 models.py로 들어간다.

from django.db import models
from django.core.validators import MinValueValidator, MaxValueValidator

from django.contrib.auth.models import User 
# 원래 맨 위 두 줄만 임포트 되어있었는데, 지금 이 바로 윗줄을 임포트해준다.
# 현재 모델에는 Ebook과 Review만 존재하는데, 그 어디에도 user 관련 테이블은 없다.
# 하지만 지금 우리는 review를 적은 바로 그 유저만 해당 리뷰 수정/삭제를 가능하게 할 것이므로
# django.contrib.auth.models 내의 User를 임포트한다. 이후 Review 모델만 수정한다.

class Review(models.Model):
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now_add=True)
    review_author = models.ForeignKey(User, on_delete=models.CASCADE)
    # 원래는 models.CharField(max_length=8, blank=True, null=True)였지만
    # 임포트한 User 모델을 사용해준다.
    review = models.TextField(blank=True, null=True)
    rating = models.PositiveIntegerField(validators=[MinValueValidator(1),
                                                     MaxValueValidator(5)])
    ebook = models.ForeignKey(Ebook,
                              on_delete=models.CASCADE,
                              related_name="reviews")

    def __str__(self):
        return str(self.rating)

2) models.py에 변화가 생기면 반드시 해야하는 일, python manage.py makemigrations를 해준다. 이 때, 왜 default값 지정안하냐고 에러 비슷한 것이 뜰건데, 1번을 눌러 대충 괜찮은 default를 지정하는척하면서 None으로 입력한다. (실제 강사가 한 방식)

3) python manage.py migrate
4) ebooksapi/ebooks/api/serializers.py의 ReviewSerializer를 수정한다.

from rest_framework import serializers
from ebooks.models import Ebook, Review


class ReviewSerializer(serializers.ModelSerializer):

    review_author = serializers.StringRelatedField(read_only=True)
    # 왜 read_only=True를 하냐면, request.user와 review_author field를 자동으로 묶고 싶기 때문 

    class Meta:
        model = Review
        exclude = ("ebook", )
        #fields = "__all__"

5) ebooksapi/ebooks/api/views.py의 ReviewCreateAPIView를 수정한다.

class ReviewCreateAPIView(generics.CreateAPIView):
    queryset = Review.objects.all()
    serializer_class = ReviewSerializer
    permission_classes = [permissions.IsAuthenticatedOrReadOnly]
    # 3) 이 permission이 꼭 있어야 하는게, 인증된 유저가 아니면 앞의 1), 2) 과정은 진행되어서는 안되기 때문이다.

    def perform_create(self, serializer):
        ebook_pk = self.kwargs.get("ebook_pk")
        ebook = get_object_or_404(Ebook, pk=ebook_pk)
       
        review_author = self.request.user
        # 1) 위에서 말했듯 request.user와 review_author를 자동으로 결합시킬 수 있는 코드를 짠다

        review_queryset = Review.objects.filter(
            ebook=ebook, review_author=review_author)

        if review_queryset.exists():
            raise ValidationError("You've already reviewed this Ebook!")
            
        # 4) 한 유저가 한 책에 대해 리뷰를 작성한다면 한 건일테니, 
        # 두 건 이상 작성하지 못하도록 조건을 건다.
        # 이 제약을 걸기위해서는 맨 윗단에 from rest_framework.exceptions import ValidationError를 임포트해줘야 한다.


        serializer.save(ebook=ebook, review_author=review_author)
        # 2) 그리고 원래는 ebook=ebook만을 인자로 취했지만 review_author=review_author(이게 방금 자동으로 결합된 review_author)를 써준다.

6) ebooksapi/ebooks/api/permissions.py 로 돌아가서 Review를 더 안전하게 해줄 완벽한 단 하나의 permission을 더 추가해보자.

class IsReviewAuthorOrReadOnly(permissions.BasePermission):

    def has_object_permission(self, request, view, obj):
        if request.method in permissions.SAFE_METHODS:
            return True

        return obj.review_author == request.user
        # 해당 리뷰를 쓴 사람이 접근하지 않으면 False값을 리턴하여 리뷰 수정/삭제를 막는다

7) 다시 ebooksapi/ebooks/api/views.py의 ReviewCreateAPIView를 수정한다. 방금 permissions.py에서 새로운 class를 만들었으므로 import단부터 바꿔야한다.

from rest_framework import generics
from rest_framework import permissions
from rest_framework.exceptions import ValidationError
from rest_framework.generics import get_object_or_404

from ebooks.models import Ebook, Review
from ebooks.api.permissions import IsAdminUserOrReadOnly, IsReviewAuthorOrReadOnly # 방금 생성한 IsReviewAuthorOrReadOnly 임포트 추가
from ebooks.api.serializers import EbookSerializer, ReviewSerializer


class ReviewCreateAPIView(generics.CreateAPIView):
    queryset = Review.objects.all()
    serializer_class = ReviewSerializer
    permission_classes = [permissions.IsAuthenticatedOrReadOnly]

    def perform_create(self, serializer):
        ebook_pk = self.kwargs.get("ebook_pk")
        ebook = get_object_or_404(Ebook, pk=ebook_pk)
       
        review_author = self.request.user

        review_queryset = Review.objects.filter(
            ebook=ebook, review_author=review_author)

        if review_queryset.exists():
            raise ValidationError("You've already reviewed this Ebook!")

        serializer.save(ebook=ebook, review_author=review_author)
        

class ReviewDetailAPIView(generics.RetrieveUpdateDestroyAPIView):
    queryset = Review.objects.all()
    serializer_class = ReviewSerializer
    permission_classes = [IsReviewAuthorOrReadOnly]
    # 당연히 이부분에 IsReviewAuthorOrReadOnly가 필요하게 되므로, permission_classes로 추가해준다.
    # permission에 정의한대로, 작성자가 아니면 수정/삭제가 불가능하게 된다.
profile
Backend 개발 학습 아카이빙 블로그입니다. (현재는 작성하지 않습니다.)

0개의 댓글