리소스 라우팅은 특정 리소스 컨트롤러에 대한 모든 공통 경로를 신속하게 선언할 수 있게 해줍니다. 인덱스 등의 개별 경로를 선언하는 대신, 리소스 경로는 한 줄의 코드로 이를 선언합니다.
— Ruby on Rails Documentation
Rails와 같은 일부 웹 프레임워크는 애플리케이션의 URL이 들어오는 요청을 처리하는 로직과 어떻게 연결되는지를 자동으로 결정하는 기능을 제공합니다.
REST 프레임워크는 Django에 자동 URL 라우팅을 지원하여, 뷰 로직을 일관되고 간단하게 URL 세트에 연결할 수 있는 방법을 제공합니다.
다음은 SimpleRouter를 사용하는 간단한 URL 설정 예시입니다:
from rest_framework import routers
router = routers.SimpleRouter()
router.register(r'users', UserViewSet)
router.register(r'accounts', AccountViewSet)
urlpatterns = router.urls
register() 메서드에는 두 가지 필수 인자가 있습니다:
선택적인 추가 인자:
queryset 속성에 따라 자동으로 생성됩니다. 뷰셋에 queryset 속성이 없는 경우에는 basename을 명시적으로 지정해야 합니다.위 예시는 다음과 같은 URL 패턴을 생성합니다:
^users/$ 이름: 'user-list'^users/{pk}/$ 이름: 'user-detail'^accounts/$ 이름: 'account-list'^accounts/{pk}/$ 이름: 'account-detail'basename 인자는 뷰 이름 패턴의 시작 부분을 지정하는 데 사용됩니다. 위 예에서는 user 또는 account가 이에 해당합니다.
보통은 basename 인자를 지정할 필요가 없지만, 뷰셋에 커스텀 get_queryset 메서드를 정의한 경우, 뷰셋에 .queryset 속성이 설정되지 않을 수 있습니다. 이 경우, basename 인자가 명시적으로 지정되지 않으면 오류가 발생할 수 있습니다:
'basename' 인자가 지정되지 않았고, 뷰셋에서 '.queryset' 속성을 찾을 수 없어 자동으로 이름을 결정할 수 없었습니다.
이는 모델 이름에서 자동으로 결정할 수 없기 때문에, 뷰셋을 등록할 때 basename 인자를 명시적으로 설정해야 한다는 의미입니다.
include와 라우터 사용하기라우터 인스턴스의 .urls 속성은 표준 URL 패턴 리스트입니다. 이 URL들을 포함하는 몇 가지 다른 스타일이 있습니다.
예를 들어, 기존의 뷰 목록에 router.urls를 추가할 수 있습니다:
router = routers.SimpleRouter()
router.register(r'users', UserViewSet)
router.register(r'accounts', AccountViewSet)
urlpatterns = [
path('forgot-password/', ForgotPasswordFormView.as_view()),
]
urlpatterns += router.urls
또는 Django의 include 함수를 사용할 수도 있습니다:
urlpatterns = [
path('forgot-password', ForgotPasswordFormView.as_view()),
path('', include(router.urls)),
]
어플리케이션 네임스페이스와 함께 include를 사용할 수도 있습니다:
urlpatterns = [
path('forgot-password/', ForgotPasswordFormView.as_view()),
path('api/', include((router.urls, 'app_name'))),
]
또는 어플리케이션과 인스턴스 네임스페이스를 함께 사용할 수도 있습니다:
urlpatterns = [
path('forgot-password/', ForgotPasswordFormView.as_view()),
path('api/', include((router.urls, 'app_name'), namespace='instance_name')),
]
더 자세한 내용은 Django의 URL 네임스페이스 문서와 include API 레퍼런스를 참조하세요.
주의: 하이퍼링크된 시리얼라이저를 네임스페이싱할 경우, 시리얼라이저의 view_name 매개변수가 네임스페이스를 정확히 반영하도록 해야 합니다. 위 예시에서는 사용자 상세 뷰에 하이퍼링크된 필드의 경우 view_name='app_name:user-detail'과 같은 매개변수를 포함해야 합니다.
자동 view_name 생성은 %(model_name)-detail과 같은 패턴을 사용합니다. 모델 이름이 충돌하지 않는 한, 하이퍼링크된 시리얼라이저를 사용할 때는 Django REST Framework 뷰를 네임스페이싱하지 않는 것이 더 나을 수 있습니다.
뷰셋은 메서드에 @action 데코레이터를 사용하여 추가 액션을 라우팅할 수 있습니다. 이 추가 액션들은 생성된 경로에 포함됩니다. 예를 들어, UserViewSet 클래스에서 set_password 메서드를 사용한 경우:
from myapp.permissions import IsAdminOrIsSelf
from rest_framework.decorators import action
class UserViewSet(ModelViewSet):
...
@action(methods=['post'], detail=True, permission_classes=[IsAdminOrIsSelf])
def set_password(self, request, pk=None):
...
다음과 같은 경로가 생성됩니다:
^users/{pk}/set_password/$'user-set-password'기본적으로, URL 패턴은 메서드 이름을 기준으로 하며, URL 이름은 ViewSet.basename과 하이픈으로 연결된 메서드 이름의 조합입니다. 이 값들을 기본값으로 사용하지 않으려면 @action 데코레이터에 url_path와 url_name 인자를 제공할 수 있습니다.
예를 들어, 커스텀 액션의 URL을 ^users/{pk}/change-password/$로 변경하려면 다음과 같이 작성할 수 있습니다:
from myapp.permissions import IsAdminOrIsSelf
from rest_framework.decorators import action
class UserViewSet(ModelViewSet):
...
@action(methods=['post'], detail=True, permission_classes=[IsAdminOrIsSelf],
url_path='change-password', url_name='change_password')
def set_password(self, request, pk=None):
...
위 예시는 이제 다음과 같은 URL 패턴을 생성합니다:
^users/{pk}/change-password/$'user-change_password'이 라우터는 목록, 생성, 검색, 수정, 부분 수정, 삭제와 같은 표준 액션을 위한 경로를 포함합니다. 뷰셋은 @action 데코레이터를 사용하여 추가 메서드를 라우팅할 수도 있습니다.
| URL 스타일 | HTTP 메서드 | 액션 | URL 이름 |
|---|---|---|---|
{prefix}/ | GET | list | {basename}-list |
| POST | create | ||
{prefix}/{url_path}/ | GET | @action(detail=False)로 데코레이트된 메서드 | {basename}-{url_name} |
{prefix}/{lookup}/ | GET | retrieve | {basename}-detail |
| PUT | update | ||
| PATCH | partial_update | ||
| DELETE | destroy | ||
{prefix}/{lookup}/{url_path}/ | GET | @action(detail=True)로 데코레이트된 메서드 | {basename}-{url_name} |
기본적으로 SimpleRouter가 생성한 URL에는 슬래시가 뒤에 붙습니다. 이 동작은 라우터를 인스턴스화할 때 trailing_slash 인자를 False로 설정하여 변경할 수 있습니다. 예를 들어:
router = SimpleRouter(trailing_slash=False)
이제 URL 패턴에서 슬래시가 생략됩니다. Django에서는 슬래시가 관례이지만, Rails와 같은 일부 다른 프레임워크에서는 기본적으로 사용되지 않습니다. 어느 스타일을 사용할지는 선호도의 문제일 뿐만 아니라, 일부 JavaScript 프레임워크는 특정 라우팅 스타일을 기대할 수 있습니다.
기본적으로 SimpleRouter가 생성한 URL은 정규 표현식을 사용합니다. 이 동작은 라우터를 인스턴스화할 때 use_regex_path 인자를 False로 설정하여 변경할 수 있으며, 이 경우 경로 변환기가 사용됩니다. 예를 들어:
router = SimpleRouter(use_regex_path=False)
주의: use_regex_path=False는 Django 2.x 이상에서만 동작하며, 이 기능은 2.0.0 버전에 도입되었습니다. 릴리스 노트를 참조하세요.
라우터는 슬래시 및 마침표 문자를 제외한 모든 문자를 포함하는 조회 값을 일치시킵니다. 더 제한적이거나 덜 제한적인 조회 패턴을 원할 경우, 뷰셋에서 lookup_value_regex 속성을 설정하거나 경로 변환기를 사용할 경우 lookup_value_converter를 설정하세요. 예를 들어, 조회를 유효한 UUID로 제한할 수 있습니다:
class MyModelViewSet(mixins.RetrieveModelMixin, viewsets.GenericViewSet):
lookup_field = 'my_model_id'
lookup_value_regex = '[0-9a-f]{32}'
class MyPathModelViewSet(mixins.RetrieveModelMixin, viewsets.GenericViewSet):
lookup_field = 'my_model_uuid'
lookup_value_converter = 'uuid'
이 라우터는 SimpleRouter와 유사하지만, 추가적으로 모든 목록 뷰에 대한 하이퍼링크를 포함하는 기본 API 루트 뷰를 제공합니다. 또한 선택적으로 .json 형식의 접미사를 사용하여 경로를 생성할 수 있습니다.
| URL 스타일 | HTTP 메서드 | 액션 | URL 이름 |
|---|---|---|---|
| [.format] | GET | 자동 생성된 루트 뷰 | api-root |
| {prefix}/[.format] | GET | 목록 보기 | {basename}-list |
| POST | 생성 | ||
| {prefix}/{url_path}/[.format] | GET, 또는 methods 매개변수에 지정된 메서드 | @action(detail=False) 데코레이터된 메서드 | {basename}-{url_name} |
| {prefix}/{lookup}/[.format] | GET | 세부 보기 | {basename}-detail |
| PUT | 업데이트 | ||
| PATCH | 부분 업데이트 | ||
| DELETE | 삭제 | ||
| {prefix}/{lookup}/{url_path}/[.format] | GET, 또는 methods 매개변수에 지정된 메서드 | @action(detail=True) 데코레이터된 메서드 | {basename}-{url_name} |
SimpleRouter와 마찬가지로, URL 경로에서 슬래시를 제거하려면 trailing_slash 인수를 False로 설정하여 라우터를 인스턴스화할 수 있습니다.
router = DefaultRouter(trailing_slash=False)
커스텀 라우터를 구현할 필요는 자주 없지만, API의 URL이 특정한 방식으로 구조화되어야 하는 경우 유용할 수 있습니다. 이를 통해 URL 구조를 재사용 가능한 방식으로 캡슐화하여 각 새로운 뷰에 대해 URL 패턴을 명시적으로 작성하지 않아도 됩니다.
가장 간단한 방법은 기존의 라우터 클래스 중 하나를 서브클래싱하는 것입니다. .routes 속성은 각 뷰셋에 매핑될 URL 패턴을 템플릿하는 데 사용됩니다. .routes 속성은 Route라는 이름의 튜플 리스트입니다.
Route 튜플에 대한 인수는 다음과 같습니다:
{prefix}: 이 경로 집합에 사용할 URL 접두사.{lookup}: 단일 인스턴스와 일치하는 조회 필드.{trailing_slash}: / 또는 빈 문자열, trailing_slash 인수에 따라 달라짐.{basename}: 생성되는 URL 이름에 사용할 기본 이름.detail, basename, suffix 인수는 뷰셋의 내부 조사와 브라우저 API에서 뷰 이름과 경로 링크를 생성하는 데 사용됩니다.@action 데코레이터가 라우팅되는 방식을 커스터마이징할 수도 있습니다. .routes 리스트에 DynamicRoute 이름의 튜플을 포함하고, 목록 기반 및 세부 기반 경로에 적절하게 detail 인수를 설정합니다. DynamicRoute의 인수는 다음과 같습니다:
{url_path} 형식 문자열을 허용합니다.{basename}: 생성되는 URL 이름에 사용할 기본 이름.{url_name}: @action에 제공된 url_name.다음 예시는 목록 및 세부 액션으로만 라우팅되며, 슬래시를 사용하지 않습니다.
from rest_framework.routers import Route, DynamicRoute, SimpleRouter
class CustomReadOnlyRouter(SimpleRouter):
"""
읽기 전용 API를 위한 라우터이며, 슬래시를 사용하지 않습니다.
"""
routes = [
Route(
url=r'^{prefix}$',
mapping={'get': 'list'},
name='{basename}-list',
detail=False,
initkwargs={'suffix': 'List'}
),
Route(
url=r'^{prefix}/{lookup}$',
mapping={'get': 'retrieve'},
name='{basename}-detail',
detail=True,
initkwargs={'suffix': 'Detail'}
),
DynamicRoute(
url=r'^{prefix}/{lookup}/{url_path}$',
name='{basename}-{url_name}',
detail=True,
initkwargs={}
)
]
views.py:
class UserViewSet(viewsets.ReadOnlyModelViewSet):
"""
표준 액션을 제공하는 뷰셋
"""
queryset = User.objects.all()
serializer_class = UserSerializer
lookup_field = 'username'
@action(detail=True)
def group_names(self, request, pk=None):
"""
해당 사용자가 속한 모든 그룹 이름을 반환한다.
"""
user = self.get_object()
groups = user.groups.all()
return Response([group.name for group in groups])
urls.py:
router = CustomReadOnlyRouter()
router.register('users', UserViewSet)
urlpatterns = router.urls
| URL | HTTP 메서드 | 액션 | URL 이름 |
|---|---|---|---|
| /users | GET | 목록 보기 | user-list |
| /users/{username} | GET | 세부 보기 | user-detail |
| /users/{username}/group_names | GET | group_names | user-group-names |
더 자세한 .routes 속성 설정에 대한 예시는 SimpleRouter 클래스의 소스 코드를 참조하십시오.
사용자 정의 동작을 완전히 구현하고 싶으신 경우, BaseRouter를 상속받아 get_urls(self) 메서드를 재정의할 수 있습니다. 이 메서드는 등록된 뷰셋을 검사하고 URL 패턴 목록을 반환해야 합니다. 등록된 접두사(prefix), 뷰셋(viewset), 기본 이름(basename) 튜플은 self.registry 속성을 통해 확인할 수 있습니다.
또한, get_default_basename(self, viewset) 메서드를 재정의하거나, 뷰셋을 라우터에 등록할 때마다 기본 이름(basename) 인수를 명시적으로 설정해야 할 수도 있습니다.
다음과 같은 서드파티 패키지도 사용 가능합니다.
drf-nested-routers 패키지는 중첩된(nested) 리소스와 함께 작업할 수 있도록 라우터와 관계 필드를 제공합니다.
wq.db 패키지는 register_model() API를 포함한 고급 ModelRouter 클래스를 제공합니다. 이 패키지는 DefaultRouter를 확장하여 register_model()을 통해 모델 클래스를 등록하는 기능을 제공합니다. 이는 Django의 admin.site.register와 유사하게 동작하며, rest.router.register_model에 필수로 제공해야 할 유일한 인수는 모델 클래스입니다. URL 접두사, 시리얼라이저, 뷰셋에 대한 합리적인 기본값은 모델과 전역 설정에서 추론됩니다.
from wq.db import rest
from myapp.models import MyModel
rest.router.register_model(MyModel)
DRF-extensions 패키지는 중첩된 뷰셋을 만들 수 있는 라우터와, 사용자 정의 가능한 엔드포인트 이름을 가진 컬렉션 수준의 컨트롤러를 제공합니다.