https://www.django-rest-framework.org/tutorial/5-relationships-and-hyperlinked-apis/
지금까지 우리는 relationship을 primary key을 사용하여 표현하였다면,
이제는 hyperlinking for relationships를 사용하여 API의 cohesion&discoverability를 향상시킬 것이다.
API root를 위한 endpoint 만들기
우리는 2개의 endpoints를 가지고 있다: 'snippets', 'users'
이둘을 위한 single entry point를 만들어보자
이를 위해, regular function-based view를 사용할 것이다: @api_view decorator
# snippets/views/py
from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework.reverse import reverse
@api_view(['GET'])
def api_root(request, format=None):
return Response({
'users': reverse('user-list', request=request, format=format),
'snippets': reverse('snippet-list', request=request, format=format)
})
* reverse: REST framework가 제공하는 기능으로, fully-qualified URLs를 return한다
* URL patterns는 뒤에서 선언할 간단한 names로 식별될 것이다
URLconf에 view를 추가하자
# snippets/urls.py
urlpatterns = [
path('', views.api_root), # root
path('snippets/', views.SnippetList.as_view()),
path('snippets/<int:pk>/', views.SnippetDetail.as_view()),
path('users/', views.UserList.as_view()),
path('users/<int:pk>/', views.UserDetail.as_view()),
]
highlighted snippets를 위한 endpoint 만들기
지금까지의 API endpoints는 JSON representation을 사용했으나,
이번 highlighted snippets를 위한 endpoint는 HTMl representation을 사용할 것이다
* representation이란? -> [오늘공부] - [2020/04/10] REST/ resource / representation
REST framework가 제공하는 HTML renderer은 2가지 스타일이 있다:
1. HTML renderer using templates
2. pre-rendered HTML
여기서는 2번째 방법을 사용하도록 하자
또한, 고려해야할 것은,
code highlighting ivew를 만들 때, 우리가 사용할 concrete generic view(예: ListCreateAPIView ...)가 없다는 것이다.
우리는 object instance가 아닌 property(속성) of an object instance를 return해야한다
concrete generic view를 사용하지 않고,
base class를 사용하고, .get() 메소드를 만들 것이다.
# snippets/views.py
from rest_framework import renderers
class SnippetHighlight(generics.GenericAPIView):
queryset = Snippet.objects.all()
renderer_classes = [renderers.StaticHTMLRenderer]
def get(self, request, *args, **kwargs):
snippet = self.get_object()
return Response(snippet.highlighted)
URLconf에 view를 추가하자
# snippets/urls.py
urlpatterns = [
path('', views.api_root), # root
path('snippets/', views.SnippetList.as_view()),
path('snippets/<int:pk>/', views.SnippetDetail.as_view()),
path('users/', views.UserList.as_view()),
path('users/<int:pk>/', views.UserDetail.as_view()),
path('snippets/<int:pk>/highlight', views.SnippetHighlight.as_view()), # SnippetHighlight
]
API에 Hyperlinking하기
entity들 사이에 relationships을 다루는 일은 Web API design에 있어 어려운 부분 중 하나이다.
relationship을 표현하는데 있어 여러 방법들이 있다:
- primary keys 사용
- entities 사이에 hyperlinking 사용
- related entity에 고유의 식별 가능한 slug field 사용
- related entity의 default string representation 사용
- related entity를 부모 representation에 넣기
- custom representation 사용
REST framework는 이 모든 방법을 지원하고, 이를 forward/reverse relationships 혹은 custom managers(예: generic foreign keys)에 적용할 수 있다.
여기서는 hyperlinked style between entities를 사용하도록 할 것이다.
ModelSerializer 대신 HyperlinkedModelSerializer을 사용하자
HyperlinkedModelSerializer은 이런 특징이 있다:
1. id field를 디폴트로 가지고 있지 않는다
2. url field를 포함하는데, HyperlinkedIdentityField로 가능하다.
3. relationship은 HyperlinkedRelatedField를 사용한다(PrimaryKeyRelatedField 대신에)
snippets/serializers.py를 수정해보자
from rest_framework import serializers
from snippets.models import Snippet, LANGUAGE_CHOICES, STYLE_CHOICES
from django.contrib.auth.models import User
class SnippetSerializer(serializers.HyperlinkedModelSerializer): # HyperlinkedModelSerializer
owner = serializers.ReadOnlyField(source='owner.username') # owner field 추가
highlight = serializers.HyperlinkedIdentityField(view_name='snippet-highlight', format='html') # hyperlinked
class Meta:
model = Snippet
fields = ['url', 'id', 'highlight', 'owner', 'title', 'code', 'linenos', 'language', 'style'] # url, highlight 추가
class UserSerializer(serializers.HyperlinkedModelSerializer): # HyperlinkedModelSerializer
snippets = serializers.HyperlinkedRelatedField(many=True, view_name='snippet-detail', read_only=True) # hyperlinked
class Meta:
model = User
fields = ['url', 'id', 'username', 'snippets'] # url 추가
URL patters가 named되었는지 확인하기
hyperlinked API를 사용한다면, 다음의 URL patterns에 이름을 붙여줘야한다:
- API root는 'user-list'와 'snippet-list'를 참조해야한다
- snippet serializer은 'snippet-highlight'을 참조하는 field를 포함해야한다
- user serializer은 'snippet-detail'을 참조하는 field를 포함해야한다
- snippet&user serializer은 'url' field를 포함해야하는데, 이는 기본적으로 '{model_name}-detail'(여기서는 'snippet-detail'과 'user-detail')을 참조한다.
결론적으로, snippets/urls.py는 다음과 같아야 한다
from django.urls import path
from snippets import views
from rest_framework.urlpatterns import format_suffix_patterns
urlpatterns = format_suffix_patterns([
path('', views.api_root), # root
path('snippets/', views.SnippetList.as_view(), name='snippet-list'),
path('snippets/<int:pk>/', views.SnippetDetail.as_view(), name='snippet-detail'),
path('snippets/<int:pk>/highlight', views.SnippetHighlight.as_view(), name='snippet-highlight'), # SnippetHighlight
path('users/', views.UserList.as_view(), name='user-list'),
path('users/<int:pk>/', views.UserDetail.as_view(), name='user-detail'),
])
pagination 추가하기
Pagination이란 paging이라고도 하며, document를 여러 페이지로 나누는 것을 말한다.
즉, users와 code snippets를 위한 list views가 꽤 많은 instances를 return할 수 있기에, 이를 좀더 작은 덩어리를 나눠서 제공하는 것을 말한다.
default list style을 바꿈으로써 간단히 해결할 수 있다. tutorial/settings.py에 다음 코드를 추가해보자
# rest-framework
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 10
}
Browsing the API
테스트해보자
> python manage.py runserver
http://127.0.0.1:8000/snippets/에서 snippets의 리스트를 볼 수 있고, highlight field에서 highlighted code HTML representations을 얻을 수 있는 링크를 볼 수 있을 것이다.
'Python > Django' 카테고리의 다른 글
[Django + PostgreSQL] PostgreSQL 설치 후 Django와 연동하기 / 기존 프로젝트 수정 (0) | 2020.04.16 |
---|---|
[Django REST framework] 6. ViewSets & Routers (0) | 2020.04.12 |
[Django REST framework] 4. Authentication & Permissions (0) | 2020.04.10 |
[Django REST framework] 3. Class-based Views (0) | 2020.04.10 |
[Django REST framework] 2. Requests and Responses (0) | 2020.04.10 |