DRF Tutorial

DRF Tutorial 4: Authentication & Permissions 1

waterclean101 2023. 2. 15. 10:56

현재의 API에는 스니펫을 편집하거나 삭제할 수 있는 사용자에 대한 제한이 없다. 이를 확실히 하기 위해 몇 가지 고급 동작을 추가하자.

  • 스니펫은 항상 작성자와 연결됨
  • 인증된 사용자만 스니펫을 만들 수 있음
  • 스니펫 작성자만 스니펫을 업데이트하거나 삭제할 수 있음
  • 인증되지 않은 요청은 전체 읽기 전용 액세스 권한을 가짐

 

모델에 정보 추가하기

스니펫 모델 클래스를 몇 가지 변경하자. 먼저 몇 개의 필드를 추가한다. 그 중 하나는스니펫을 만든 사용자를 나타내는 데 사용된다. 다른 필드는 코드의 강조 표시된 HTML 표현을 저장하는 데 사용된다.

models.py의 스니펫 모델에 다음 두 필드를 추가

# snippets/models.py

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

 # related_name: reverse accessors(역접근자)의 이름을 커스텀 하는 방법. 모델 A가 모델 B foreign key(외래 키)를 가지고 있을 때 자동적으로 모델 B '모델 A_set'을 받게 된다. 여기서 related_name을 A로 설정하면 BA_set 대신 A를 사용할 수 있게 된다. 

# reverse accessors(역접근자): 예를 들어 user와 room이 있고 room이 user에 대한 foreign key를 가지고 있을 때 user의 입장에서 user를 가리키고 있는 room은 어떤 게 있는지 알고 싶을 때 역접근자를 사용한다. 역방향으로 가리키는 건 _set으로 구현된다. room_set user를 가리키는 모든 rooms의 리스트라고 할 수 있다. 장고가 모델의 이름을 가져가서 그걸 소문자로 만들고, '_set'을 추가하는데 이 작업은 자동으로 발생한다. 그런데 room_set이라는 단어를 커스텀 하고 싶다면 바로 여기서 related_name을 사용하면 된다. 즉 related_name foreign key와 사용하는 것인데 타켓 모델이 foreign key를 만드는 모델에 어떻게 접근할 수 있는지 명시하게 한다.

 

모델을 저장할 때 pygments 코드 강조 표시 라이브러리를 사용하여 강조 표시된 필드를 채우는지 확인해야 한다. 이를 위해 몇 가지를 추가적으로 import 해준다.

# snippets/models.py

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

이제 모델 클래스에 .save() 메서드를 추가할 수 있다.

def save(self, *args, **kwargs):
    """
    Use the `pygments` library to create a highlighted HTML
    representation of the code snippet.
    """
    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

 

API를 테스트하는 데 사용할 몇 가지 다른 사용자를 만들 수 있다. 가장 빠른 방법은 createsuperuser 명령을 사용하는 것이다.

python manage.py createsuperuser

 

사용자 모델에 엔드포인트 추가하기

이제 작업할 사용자가 생겼으니 해당 사용자의 값을 API에 추가하자. serializers.py에 새 serializer를 만든다.

# snippets/serializer.py

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']

'스니펫'은 사용자 모델에서 역관계(reverse relationship)이기 때문에 ModelSerializer 클래스를 사용할 때 기본적으로 포함되지 않으므로 이를 위한 명시적 필드를 추가해야 했다.


다음으로 views.py에 몇 가지 view를 추가하자. 사용자 값을 읽기 전용으로 할 것이기 때문에  ListAPIView 및 RetrieveAPIView generic class-based views를 사용한다.

# 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

 

마지막으로 URL conf에서 views를 참조하여 API에 추가해야 한다. snippets/urls.py에 다음을 추가하자.

path('users/', views.UserList.as_view()),
path('users/<int:pk>/', views.UserDetail.as_view()),