Django REST framework API 指南(6):路由

wcode發表於2018-03-05

Django REST framework API 指南(1):請求
Django REST framework API 指南(2):響應
Django REST framework API 指南(3):檢視
Django REST framework API 指南(4):通用檢視
Django REST framework API 指南(5):檢視集
Django REST framework API 指南(6):路由
Django REST framework API 指南(7):解析

官方原文連結

路由

一些 Web 框架(如 Rails)提供了一種能夠自動確定應用程式的 URL 如何對映到處理請求的功能。

REST framework 增加了對 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() 方法有兩個必須引數:

  • prefix - 設定這組路由的字首。
  • viewset - 設定對應的檢視集類。

或者,您也可以指定一個附加引數:

  • base_name - 用於建立的 URL 名稱的基礎。如果未設定,將根據檢視集的 queryset 屬性自動生成。請注意,如果檢視集不包含 queryset 屬性,則在註冊檢視集時必須設定 base_name

上面的例子會生成以下 URL 模式:

  • URL pattern: ^users/$ Name: 'user-list'
  • URL pattern: ^users/{pk}/$ Name: 'user-detail'
  • URL pattern: ^accounts/$ Name: 'account-list'
  • URL pattern: ^accounts/{pk}/$ Name: 'account-detail'

注意:base_name 引數用於指定檢視名稱模式的初始部分。在上面的例子中,是 useraccount 部分。

通常,您不需要指定 base_name 引數,但是如果您有一個檢視集定義了自定義 get_queryset 方法,那麼該檢視集可能沒有設定 .queryset 屬性。如果此時嘗試註冊該檢視,則會看到如下所示的錯誤:

'base_name' argument not specified, and could not automatically determine the name from the viewset, as it does not have a '.queryset' attribute.
複製程式碼

'base_name' 引數未指定,並且無法自動確定檢視中的名稱,因為它沒有' .queryset' 屬性。

這時候就需要在註冊檢視集時顯式設定 base_name 引數,因為它無法從模型名稱中自動確定。

使用 include 與路由

路由例項上的 .urls 屬性是一個標準的 URL patterns。關於如何包含這些 URL,有許多不同的樣式。

例如,可以將 router.urls 附加到現有檢視的列表中...

router = routers.SimpleRouter()
router.register(r'users', UserViewSet)
router.register(r'accounts', AccountViewSet)

urlpatterns = [
    url(r'^forgot-password/$', ForgotPasswordFormView.as_view()),
]

urlpatterns += router.urls
複製程式碼

另外,你也可以使用 Django 的 include 函式,比如...

urlpatterns = [
    url(r'^forgot-password/$', ForgotPasswordFormView.as_view()),
    url(r'^', include(router.urls)),
]
複製程式碼

還可以設定 namespace。

urlpatterns = [
    url(r'^forgot-password/$', ForgotPasswordFormView.as_view()),
    url(r'^api/', include(router.urls, namespace='api')),
]
複製程式碼

如果對超連結序列化器使用名稱空間,則還需要確保序列化器上的任何 view_name 引數都能正確反映名稱空間。在上面的示例中,您需要為超連結到使用者詳細資訊檢視的序列化程式欄位包含諸如 view_name='api:user-detail' 之類的引數。

額外的連結和操作

@detail_route@list_route 裝飾的 檢視上的任何方法 也將被路由。例如,在 UserViewSet 類中給出這樣的方法:

from myapp.permissions import IsAdminOrIsSelf
from rest_framework.decorators import detail_route

class UserViewSet(ModelViewSet):
    ...

    @detail_route(methods=['post'], permission_classes=[IsAdminOrIsSelf])
    def set_password(self, request, pk=None):
        ...
複製程式碼

會生成以下URL模式:

  • URL pattern: ^users/{pk}/set_password/$ Name: 'user-set-password'

如果您不想使用預設生成的 URL 模式,則可以使用 url_path 引數對其進行自定義。

例如,如果您想將我們的自定義操作的URL更改為 ^users/{pk}/change-password/$,則可以編寫:

from myapp.permissions import IsAdminOrIsSelf
from rest_framework.decorators import detail_route

class UserViewSet(ModelViewSet):
    ...

    @detail_route(methods=['post'], permission_classes=[IsAdminOrIsSelf], url_path='change-password')
    def set_password(self, request, pk=None):
        ...
複製程式碼

上面的例子現在將生成以下URL模式:

  • URL pattern: ^users/{pk}/change-password/$ Name: 'user-change-password'

如果您不想使用生成的預設名稱,則可以使用 url_name 引數對其進行自定義。

例如,如果您想將自定義操作的名稱更改為 'user-change-password',則可以編寫:

from myapp.permissions import IsAdminOrIsSelf
from rest_framework.decorators import detail_route

class UserViewSet(ModelViewSet):
    ...

    @detail_route(methods=['post'], permission_classes=[IsAdminOrIsSelf], url_name='change-password')
    def set_password(self, request, pk=None):
        ...
複製程式碼

上面的例子現在將生成以下URL模式:

  • URL pattern: ^users/{pk}/set_password/$ Name: 'user-change-password'

可以同時使用 url_pathurl_name 引數。

更多相關資訊請看 檢視集:標記額外的路由行為

API 參考

SimpleRouter

SimpleRouter 包含標準的 listcreateretrieveupdatepartial_updatedestroy action。SimpleRouter 還支援檢視集使用 @detail_route@list_route 裝飾器標記其他要路由的方法。

URL StyleHTTP MethodActionURL Name
{prefix}/GETlist{basename}-list
POSTcreate
{prefix}/{methodname}/GET, 或者由 `methods` 引數指定`@list_route` 裝飾的方法{basename}-{methodname}
{prefix}/{lookup}/GETretrieve{basename}-detail
PUTupdate
PATCHpartial_update
DELETEdestroy
{prefix}/{lookup}/{methodname}/GET, 或者由 `methods` 引數指定`@detail_route` 裝飾的方法{basename}-{methodname}

預設情況下,由 SimpleRouter 建立的 URL 附加了尾部斜槓。在例項化路由器時,可以通過將 trailing_slash 引數設定為 False 來修改此行為。例如:

router = SimpleRouter(trailing_slash=False)
複製程式碼

尾部斜槓在 Django 中是常規的,但在其他一些框架(如 Rails)中預設不使用。選擇使用哪種風格在很大程度上是一個偏好問題,儘管一些 JavaScript 框架可能會期望特定的路由風格。

SimpleRouter 將匹配包含除斜槓和句點字元以外的任何字元的 lookup 值。對於更嚴格(或寬鬆)的 lookup pattern,請在檢視集上設定 lookup_value_regex 屬性。例如,您可以將 lookup 限制為有效的 UUID:

class MyModelViewSet(mixins.RetrieveModelMixin, viewsets.GenericViewSet):
    lookup_field = 'my_model_id'
    lookup_value_regex = '[0-9a-f]{32}'
複製程式碼

DefaultRouter

DefaultRouter 與上面的 SimpleRouter 相似,但還包含一個預設的 API 根檢視,該檢視返回一個包含指向所有列表檢視的超連結的響應。它還為可選的 .json 風格格式字尾生成路由。

URL StyleHTTP MethodActionURL Name
[.format]GET自動生成的根檢視api-root
{prefix}/[.format]GETlist{basename}-list
POSTcreate
{prefix}/{methodname}/[.format]GET, 或者由 `methods` 引數指定`@list_route` 裝飾的方法{basename}-{methodname}
{prefix}/{lookup}/[.format]GETretrieve{basename}-detail
PUTupdate
PATCHpartial_update
DELETEdestroy
{prefix}/{lookup}/{methodname}/[.format]GET, 或者由 `methods` 引數指定`@detail_route` 裝飾的方法{basename}-{methodname}

注意:我在使用 3.7.7 版本時,發現要寫成 {prefix}[.format]/ 風格才能訪問,{prefix}/[.format] 風格會報 404,不知道是我設定問題還是官方更新了。

SimpleRouter 一樣,通過在例項化 DefaultRouter 時將 trailing_slash 引數設定為 False,可以刪除 URL 路徑上的尾部斜槓。

router = DefaultRouter(trailing_slash=False)
複製程式碼

自定義路由

自定義路由並不是你經常需要做的事情,但是如果你對 API 的 URL 是如何構建的有特定的要求的話,它會很有用。這樣做可以讓你以可重用的方式封裝 URL 結構,確保你不必為每個新檢視明確編寫 URL 模式。

實現自定義路由的最簡單方法是對現有路由類之一進行子類化。.routes 屬性用於對將對映到每個檢視集的 URL 模式進行模板化。.Routes 屬性是一個 Route 列表(Route 的是一個 namedtuple)。

Route 命名元組的引數:

url : 表示要路由的 URL 的字串。可以包含以下格式字串:

  • {prefix} - 路由的 URL 字首。
  • {lookup} - 匹配單個例項的 lookup field。
  • {trailing_slash} - 可以是 '/' 或空字串,具體取決於 trailing_slash 引數。

mapping : HTTP 方法名稱與檢視方法的對映

name: 用於呼叫 reverse 時的 URL 的名稱。可能包含以下格式字串:

  • {basename} - 建立的 URL 名稱的基礎。

initkwargs: 例項化檢視時應傳遞的任何其他引數的字典。注意,suffix 引數被保留用於標識檢視集型別,在生成檢視名稱和 breadcrumb 連結時使用。

自定義動態路由

你還可以自定義 @list_route@detail_route 裝飾器的路由方式。要路由這兩個裝飾器中的一個或兩個,請在 .routes 列表中包含一個 DynamicListRoute 和/或 DynamicDetailRoute(別忘了型別是 namedtuple)。

DynamicListRouteDynamicDetailRoute 的引數是:

url: 表示要路由的 URL 的字串。可以包含與 Route 相同的格式字串,並且還接受 {methodname}{methodnamehyphen} 格式的字串。

name: 用於呼叫 reverse 時的名稱。可以包含以下格式字串:{basename} , {methodname}{methodnamehyphen}

initkwargs: 例項化檢視時應傳遞的任何其他引數的字典。

舉個例子

以下示例只會路由 listretrieve action,並且不使用尾部斜槓約定。

from rest_framework.routers import Route, DynamicDetailRoute, SimpleRouter

class CustomReadOnlyRouter(SimpleRouter):
    """
    A router for read-only APIs, which doesn't use trailing slashes.
    """
    routes = [
        Route(
            url=r'^{prefix}$',
            mapping={'get': 'list'},
            name='{basename}-list',
            initkwargs={'suffix': 'List'}
        ),
        Route(
            url=r'^{prefix}/{lookup}$',
            mapping={'get': 'retrieve'},
            name='{basename}-detail',
            initkwargs={'suffix': 'Detail'}
        ),
        DynamicDetailRoute(
            url=r'^{prefix}/{lookup}/{methodnamehyphen}$',
            name='{basename}-{methodnamehyphen}',
            initkwargs={}
        )
    ]
複製程式碼

讓我們來看看 CustomReadOnlyRouter 為一個簡單的檢視集生成的路由。

views.py

class UserViewSet(viewsets.ReadOnlyModelViewSet):
    """
    A viewset that provides the standard actions
    """
    queryset = User.objects.all()
    serializer_class = UserSerializer
    lookup_field = 'username'

    @detail_route()
    def group_names(self, request, pk=None):
        """
        Returns a list of all the group names that the given
        user belongs to.
        """
        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 Method Action URL Name
/users GET list user-list
/users/{username} GET retrieve user-detail
/users/{username}/group-names GET group_names user-group-names

有關設定 .routes 屬性的另一個示例,請參閱 SimpleRouter 類的原始碼。

自定義路由器進階

如果想提供完全自定義的行為,可以繼承 BaseRouter 並覆蓋 get_urls(self) 方法。該方法應檢查已註冊的檢視集並返回一組 URL 模式。可以通過訪問 self.registry 屬性來檢查註冊的 prefix,viewset 和 basename tuples。

你可能還想覆蓋 get_default_base_name(self,viewset)方法,或者在向路由註冊檢視集時始終顯式設定 base_name 引數。

第三方軟體包

以下是可用的第三方包。

DRF Nested Routers

ModelRouter (wq.db.rest)

DRF-extensions

相關文章