7. Relationships & Hyperlinked APIs
1. Creating an endpoint for the root of our API
- 현재 '스니펫' 및 '사용자'에 대한 엔드포인트가 있지만 API에 대한 엔트리 포인트는 없다.
- 먼저, 엔드포인트와 엔트리 포인트의 차이점은 다음과 같다.
1] 엔드포인트
http://example.com/api/login
http://example.com/api/accounts
http://example.com/api/cart/items
2] 엔트리 포인트
http://example.com/api
- 현재 튜토리얼에서는 각 엔드포인트에 접근할 때
http://127.0.0.1:8000/snippets/
및http://127.0.0.1:8000/users/
를 입력한다. - 이렇게 입력한다는 것은 엔트리 포인트를 의미하는 것 같지만,
tutorial/urls.py
파일 내의urlpatterns
를 다음과 같이 정의했기 때문에 엔드포인트를 의미한다.
include()
로snippets
앱의 URLConf를 사용하고 있기 때문에snippets/urls.py
파일 내의urlpatterns
에 정의된'/snippets/'
및'/users/'
가 바로 호출될 수 있는 것이다.
- 다시 돌아와서, 엔트리 포인트를 생성하기 위해 함수 기반의 뷰와 앞서 소개한
@api_view
데코레이터를 사용한다. snippets/views.py
파일에 다음의 코드를 추가한다.
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),
}
)
- 정규화된 URL을 반환하기 위해 REST framework의
reverse
함수를 사용한다. - 그리고 URL 패턴은 나중에
snippets/urls.py
파일에서 선언할 이름으로 식별된다.
2. Creating an endpoint for the highlighted snippets
- REST framework에서 제공하는 두 가지 스타일의 HTML 렌더러가 있다.
- 하나는 템플릿을 사용하여 렌더링된 HTML을 처리하기 위한 것이고, 다른 하나는 미리 렌더링된 HTML을 처리하기 위한 것이다.
- 코드 강조 뷰를 생성할 때 고려해야 할 다른 사항은 사용할 수 있는 기존의 구체적인 일반(Generic) 뷰가 없다는 것이다.
- 현재 API는 객체 인스턴스를 반환하지 않고 대신 객체 인스턴스의 속성을 반환하고 있다.
- 구체적인 일반 뷰를 사용하는 대신 인스턴스를 나타내는 기본 클래스를 사용하고 고유한
.get()
메서드를 만들기 위해snippets/views.py
파일에 다음의 코드를 추가한다.
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에 생성한 새 뷰를 추가해야 한다.
- 먼저,
snippets/urls.py
파일에 새 API 루트에 대한 URL 패턴을 추가한다.
- 그리고 스니펫 강조에 대한 URL 패턴을 추가한다.
3. Hyperlinking our API
- 엔터티 간의 관계를 처리하는 것은 웹 API 디자인의 가장 어려운 측면 중 하나이다.
- 관계를 나타내기 위해 선택할 수 있는 여러 가지 방법이 있다.
1] 기본키 사용
2] 엔터티 간 하이퍼링크 사용
3] 관련 엔터티에서 고유한 식별 슬러그 필드 사용
4] 관련 엔터티의 기본 문자열 표현 사용
5] 상위 표현 내부에 관련 엔터티 중첩
6] 다른 사용자 정의 표현
- REST framework는 위와 같은 스타일을 지원하며 정방향 또는 역방향 관계에 적용하거나 일반 외래키와 같은 사용자 정의 관리자에 적용할 수 있다.
- 엔터티 간 하이퍼링크된 스타일을 사용하고 싶은 경우 이를 위해 기존
ModelSerializer
대신HyperlinkedModelSerializer
를 확장하도록 직렬 변환기를 수정한다.
HyperlinkedModelSerializer
에는ModelSerializer
와 다른 차이점이 있는데 다음과 같다.
1] 기본적으로 id
필드를 포함하지 않는다.
2] HyperlinkedIdentityField
를 사용하는 url
필드가 포함된다.
3] 관계(Relationships)는 PrimaryKeyRelatedField
대신 HyperlinkedRelatedField
를 사용한다.
snippets/serializers.py
파일을 열어 하이퍼링크를 사용하도록 기존 직렬 변환기를 다시 작성한다.
snippets/serializers.py
from rest_framework import serializers
from snippets.models import Snippet
from django.contrib.auth.models import User
class SnippetSerializer(serializers.HyperlinkedModelSerializer):
owner = serializers.ReadOnlyField(source="owner.username")
highlight = serializers.HyperlinkedIdentityField(
view_name="snippet-highlight", format="html"
)
class Meta:
model = Snippet
fields = [
"url",
"id",
"highlight",
"owner",
"title",
"code",
"linenos",
"language",
"style",
]
class UserSerializer(serializers.HyperlinkedModelSerializer):
snippets = serializers.HyperlinkedRelatedField(
many=True, view_name="snippet-detail", read_only=True
)
class Meta:
model = User
fields = ["url", "id", "username", "snippets"]
- 새로운
highlight
필드를 추가했다. - 이 필드는
"snippet-detail"
URL 패턴 대신"snippet-highlight"
URL 패턴을 가리키는 것을 제외하고는url
필드와 동일한 유형이다. ".json"
과 같은 형식 접미사 URL을 포함했기 때문에 반환되는 형식 접미사 하이퍼링크가".html"
접미사를 사용해야 함을highlight
필드에 표시해야 한다.
4. Making sure our URL patterns are named
- 하이퍼링크된 API를 사용하려면 URL 패턴의 이름을 지정해야 한다.
- 어떤 URL 패턴에 이름을 지정해야 하는지 살펴보자.
1] API의 루트는 "user-list"
및 "snippet-list"
를 참조한다.
2] 스니펫 직렬 변환기는 "snippet-highlight"
를 참조하는 필드를 포함한다.
3] 사용자 직렬 변환기는 "snippet-detail"
을 참조하는 필드가 포함한다.
4] 스니펫 및 사용자 직렬 변환기에는 기본적으로 "{model_name}-detail"
을 참조하는 "url"
필드가 포함되어 있으며, 이 경우에는 "snippet-detail"
및 "user-detail"
이 된다.
- 모든 이름을 URLConf에 추가한 최종
snippets/urls.py
파일은 다음과 같다.
snippets/urls.py
from django.urls import path
from rest_framework.urlpatterns import format_suffix_patterns
from snippets import views
urlpatterns = [
path("", views.api_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",
),
path("users/", views.UserList.as_view(), name="user-list"),
path("users/<int:pk>/", views.UserDetail.as_view(), name="user-detail"),
]
urlpatterns = format_suffix_patterns(urlpatterns)
5. Adding pagination
- 사용자 및 코드 스니펫에 대한 목록 뷰는 많은 인스턴스를 반환할 수 있으므로 결과에 페이지를 매기고 API 클라이언트가 각 개별 페이지를 단계별로 실행할 수 있도록 해보자.
tutorial/settings.py
파일의 맨 아래에 다음의 설정을 추가한다.
tutorial/settings.py
REST_FRAMEWORK = {
"DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.PageNumberPagination",
"PAGE_SIZE": 10,
}
- REST framework의 설정은 모두
REST_FRAMEWORK
라는 Pythondict
타입의 설정으로 네임스페이스가 지정되어 다른 프로젝트 설정과 잘 구분된다.
6. Browsing the API
- 웹 브라우저를 열고 탐색 가능한 API로 이동하면 이제 링크를 따라가면서 API를 탐색할 수 있다.