Inertia

Django Rest Framework

요즘 추세인 REST + {Angular, React,} 등을 만들기 위해서는 백엔드가 rest api가 되어야 하는데 그것을 Django Frameowrk위에서 구현한 것이 Django Rest Framework이다. 기존에 Django에 관한 post를 올렸는데, REST API backend를 가지게 되면 안드로이드/iOS 어플등도 지원할 수 있게 되고 테스트도 쉽게 만들 수 있어 장점이 있다.

아래애서 사용할 Snippet 모델을 아래 처럼 정의한다.

1
2
3
4
5
6
7
8
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(User, on_delete=models.CASCADE)

Serializer

우선 이전 포스트에서 Model의 개념은 나왔고, Serializer개념을 이해해야 한다. Model 이 Serializer를 통해서 request/response를 받는다고 생각하면 된다. Serializer는 어떻게 매핑하여 클라이언트에게 돌려줄지, 클라이언트에서 받은 데이터를 어떻게 가공하여 모델에 집어넣을지를 맵핑해주는 레이어라고 생각하면 된다.

예를 들어, 위의 Snippet 모델에 owner field는, DB에는 primary key인 int로 저장 된다. 이것을 REST API로 보낼때는 int로 돌려줄 수도 있고, 아니면 owner의 정보를 볼 수 있는 url(HyperlinkedRelatedField)로 보내줄 수 도 있다. 이렇든 같은 데이터라도 어떤 형식으로 보내줄지는 요구사항마다 달라질 수 있다. 그래서 이것을 유연하게 대처하도록 Serializer라는 개념으로 추상화 했다.

1
2
read (from client to server): request -> serializer -> model
write(from server to client): model -> serializer -> response

fields vs read_only_fields

fields에 명시된 것은 get/post 시에 나타날 field들을 명시하는 것이다. get에는 필요하지만, post/put시에는 넣을수 없는 정보는 read_\only_fields 로 명시해 주면 된다.

아래 예저의 경우 get시에는 user 필드도 받지만 post시에는 넣어주지 않아도 된다.

1
2
3
4
5
class SnippetSerializer(serializers.ModelSerializer):
class Meta:
model = Snippet
fields = ('id', 'title', 'code', 'linenos', 'language', 'style', 'user')
read_only_fields = ('user', )

ViewSet

ViewSet은 Serializer를 통한 오브젝트를 Json으로 렌더링하거나 html로 렌더링을 담당하는 View 이다. 보통은 Model기반으로 하는 ModelViewSet을 많이 사용한다. get/post등의 default implementation이 있기 때문에 대부분은 그대로 사용하면 된다.

perform_create는 post로 실제 모델을 생성하기 전에 불리는 콜백인데, 여기서 POST에서 전달받지 못한 필드들을 초기화 해줄수 있다. 여기서는 owner정보를 채우고 있다.

permission_classes는 access control를 위해서 사용되는데, 로그인한 사용자들에게만 POST를 허용하고 READ는 다 허용하겠다는 정책이 permisson.IsAuthenticatedOrReadOnly이고, owner가 write권한을 아니면 read권한만 주는 것이 IsOwnerOrReadOnly 퍼미션이다. 여기서 read라 함은 get/list 이고, write는 post/put을 얘기한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class SnippetViewSet(viewsets.ModelViewSet):
"""
This viewset automatically provides `list`, `create`, `retrieve`,
`update` and `destroy` actions.

Additionally we also provide an extra `highlight` action.
"""
queryset = Snippet.objects.all()
serializer_class = SnippetSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly,
IsOwnerOrReadOnly,)

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

Additional filtering

Snippet list 중 내가 만든 Snippet만 보려고 할때는 어떻게 해야 할까. 우선 query param으로 mine=true이런식으로 요청을 한다고 하자. 위의 SnippetViewSet 에서 qet_queryset 함수를 오버라이드하면 된다. 아래처럼.

1
2
3
4
5
6
def get_queryset(self):
queryset = Snippet.objects.all()
mine = self.request.query_params.get('mine', None)
if mine is not None:
queryset = queryset.filter(owner=self.request.user)
return queryset