Django REST Framework 뽀개기 3

marco x brown·2020년 5월 25일
0

인증과 허가

그 동안 작성해왔던 API는 누가 어떤 액션을 취하는지에 대한 제약사항이 따로 없었다. 아래와 같이 인증, 허가와 관련된 몇 가지 조건들이 포함된 기능들을 추가해보자.

  • 코드 스니펫은 항상 작성자와 연관되어 있다.
  • 인증된 사용자만 스니펫을 생성할 수 있다.
  • 특정 스니펫은 작성자만이 수정하거나 삭제할 수 있다.
  • 인증되지 않은 요청은 read-only 접근만을 허용한다.

모델에 정보 추가

처음에 작성했던 Snippet 모델 클래스에 필드 몇 개를 추가하여 약간 변화시켜줄 것이다.
하나는 누가 스니펫을 작성했는지를 나타내는 owner, 다른 하나는 하이라이트 처리된 HTML 코드를 저장할 hightlighted 필드이다.

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

또 모델이 저장될 때, pygments라는 코드 하이라이팅 라이브러를 사용하여 hightlighted 필드를 채울 수 있는지 확인해야 한다.

pygments 라이브러리의 모듈들을 import 해주고, save 메소드를 정의한다.

# models.py

from django.db import models
from pygments.lexers import get_all_lexers, get_lexer_by_name
from pygments.styles import get_all_styles
from pygments.formatters.html import HtmlFormatter
from pygments import hightlight

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):
    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)
    owner = models.ForeignKey('auth.User', related_name='snippets', on_delete=models.CASCADE)
    hightlighted = models.TextField()

    class Meta:
        ordering = ['created']

    def save(self, *args, **kwargs):
        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.hightlighted = hightlight(self.code, lexer, formatter)

        super(Snippet, self).save(*args, **kwargs)

2개 필드를 추가하여 모델이 변경되었으므로 makemigrations 커맨드를 통해 갱신시키자.

$ ./manage.py makemigrations
$ ./manage.py migrate

Admin 계정도 만들어주자.

$ ./manage.py createsuperuser

User 모델에 엔드포인트 추가

API response 데이터의 각 스니펫에 사용자도 추가하기 위해 새로운 시리얼라이저를 추가할 것이다.

# models.py

from rest_framework import serializers
from django.contrib.auth.models import User

from snippets.models import Snippet, LANGUAGE_CHOICES, STYLE_CHOICES


class SnippetSerializer(serializers.ModelSerializer):
    class Meta:
        model = Snippet
        fields = ['id', 'title', 'code', 'linenos', 'language', 'style']


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

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

UserSerializer에서의 snippetsUser로부터의 Reverse relationship이므로 ModelSerializer를 사용할 때 자동으로 만들어지지 않는다. 필드를 명시적으로 추가해줘야 한다.

API에서 사용자 정보를 확인할 수 있도록 하기 위해 2개의 view를 추가하자. ListAPIViewRetrieveAPIView Generic CBV를 사용한다.

아, UserSerializer도!

# views.py

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

from snippets.serializers import SnippetSerializer, UserSerializer


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


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

views도 다 추가했고 남은 건 URL conf이다. urls.py 모듈도 만져주자.

# urls.py

...

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

다음 포스트에서는 스니펫과 사용자를 연결하는 작업을 하려 한다.

빠잇.

profile
개발자로 크는 중

0개의 댓글