routers.py

路由器

资源路由允许你快速声明给定资源控制器的所有常见路由。无需为你的索引声明单独的路由...资源路由在单行代码中声明它们。

Ruby on Rails 文档

一些 Web 框架(如 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() 方法有两个必需参数

  • prefix - 用于此组路由的 URL 前缀。
  • viewset - 视图集类。

此外,你还可以指定一个附加参数

  • basename - 用于创建的 URL 名称的基础。如果未设置,则会根据视图集的 queryset 属性(如果存在)自动生成基础。请注意,如果视图集不包含 queryset 属性,则必须在注册视图集时设置 basename

上面的示例将生成以下 URL 模式

  • URL 模式:^users/$ 名称:'user-list'
  • URL 模式:^users/{pk}/$ 名称:'user-detail'
  • URL 模式:^accounts/$ 名称:'account-list'
  • URL 模式:^accounts/{pk}/$ 名称:'account-detail'

注意basename 参数用于指定视图名称模式的初始部分。在上面的示例中,即 useraccount 部分。

通常,你不需要指定 basename 参数,但如果你有一个视图集,其中定义了自定义 get_queryset 方法,则视图集可能没有设置 .queryset 属性。如果你尝试注册该视图集,你将看到类似这样的错误

'basename' argument not specified, and could not automatically determine the name from the viewset, as it does not have a '.queryset' attribute.

这意味着你需要在注册视图集时显式设置 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 框架视图进行命名空间处理。


额外操作的路由

视图集可以通过使用 @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):
        ...

将生成以下路由

  • URL 模式:^users/{pk}/set_password/$
  • URL 名称:'user-set-password'

默认情况下,URL 模式基于方法名称,URL 名称是 ViewSet.basename 和连字符分隔的方法名称的组合。如果您不想对这两个值中的任何一个使用默认值,则可以向 @action 装饰器提供 url_pathurl_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 模式

  • URL 路径:^users/{pk}/change-password/$
  • URL 名称:'user-change_password'

API 指南

SimpleRouter

此路由器包含标准 listcreateretrieveupdatepartial_updatedestroy 操作的路由。视图集还可以使用 @action 装饰器标记要路由的其他方法。

URL 样式HTTP 方法操作URL 名称
{prefix}/获取列表{basename}-列表
发布创建
{prefix}/{url_path}/获取,或由 `methods` 参数指定`@action(detail=False)` 修饰的方法{basename}-{url_name}
{prefix}/{lookup}/获取检索{basename}-详情
更新更新
补丁部分更新
删除销毁
{prefix}/{lookup}/{url_path}/获取,或由 `methods` 参数指定`@action(detail=True)` 修饰的方法{basename}-{url_name}

默认情况下,SimpleRouter 创建的 URL 会附加一个尾部斜杠。可以在实例化路由器时将 trailing_slash 参数设置为 False 来修改此行为。例如

router = SimpleRouter(trailing_slash=False)

尾部斜杠在 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_converter 上设置 lookup_value_regex 属性。例如,您可以将查找限制为有效的 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'

DefaultRouter

此路由器与上述 SimpleRouter 类似,但另外包含一个默认 API 根视图,该视图返回一个包含指向所有列表视图的超链接的响应。它还为可选的 .json 样式格式后缀生成路由。

URL 样式HTTP 方法操作URL 名称
[.format]获取自动生成的根视图api-root
{prefix}/[.format]获取列表{basename}-列表
发布创建
{prefix}/{url_path}/[.format]获取,或由 `methods` 参数指定`@action(detail=False)` 修饰的方法{basename}-{url_name}
{prefix}/{lookup}/[.format]获取检索{basename}-详情
更新更新
补丁部分更新
删除销毁
{prefix}/{lookup}/{url_path}/[.format]获取,或由 `methods` 参数指定`@action(detail=True)` 修饰的方法{basename}-{url_name}

SimpleRouter 一样,可以通过在实例化路由器时将 trailing_slash 参数设置为 False 来移除 URL 路由上的尾部斜杠。

router = DefaultRouter(trailing_slash=False)

自定义路由

实现自定义路由器不是经常需要做的事情,但如果您对 API 的 URL 结构有特定要求,它会很有用。这样做允许您以可重用的方式封装 URL 结构,确保您不必为每个新视图显式编写 URL 模式。

实现自定义路由器的最简单方法是子类化现有路由器类之一。.routes 属性用于设置模板 URL 模式,这些模式将映射到每个视图集。.routes 属性是 Route 命名元组的列表。

Route 命名元组的参数是

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

  • {prefix} - 用于此组路由的 URL 前缀。
  • {lookup} - 用于与单个实例匹配的查找字段。
  • {trailing_slash} - 根据 trailing_slash 参数,为 '/' 或空字符串。

mapping:HTTP 方法名称到视图方法的映射

name:URL 在 reverse 调用中使用的名称。可能包括以下格式字符串

  • {basename} - 用于创建的 URL 名称的基础。

initkwargs:实例化视图时应传递的任何其他参数的字典。请注意,detailbasenamesuffix 参数保留用于视图集自省,并且还由可浏览 API 用于生成视图名称和面包屑链接。

自定义动态路由

您还可以自定义 @action 装饰器的路由方式。在 .routes 列表中包含 DynamicRoute 命名元组,根据基于列表的路由和基于详细信息的路由设置 detail 参数。除了 detail 之外,DynamicRoute 的参数还有

url:表示要路由的 URL 的字符串。可能包括与 Route 相同的格式字符串,此外还接受 {url_path} 格式字符串。

name:URL 在 reverse 调用中使用的名称。可能包括以下格式字符串

  • {basename} - 用于创建的 URL 名称的基础。
  • {url_name} - 提供给 @actionurl_name

initkwargs:实例化视图时应传递的任何其他参数的字典。

示例

以下示例仅路由到 listretrieve 操作,并且不使用尾部斜杠约定。

from rest_framework.routers import Route, DynamicRoute, 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',
            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={}
        )
    ]

让我们来看看我们的 CustomReadOnlyRouter 为一个简单的视图集生成的路由。

views.py:

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

    @action(detail=True)
    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

将生成以下映射...

URLHTTP 方法操作URL 名称
/users获取列表user-list
/users/{username}获取检索user-detail
/users/{username}/group_names获取group_namesuser-group-names

有关设置 .routes 属性的另一个示例,请参阅 SimpleRouter 类的源代码。

高级自定义路由

如果您想要提供完全自定义的行为,则可以覆盖 BaseRouter 并覆盖 get_urls(self) 方法。该方法应检查已注册的视图集并返回 URL 模式列表。可以通过访问 self.registry 属性来检查已注册的前缀、视图集和基本名称元组。

您可能还想覆盖 get_default_basename(self, viewset) 方法,或者始终在使用路由器注册视图集时明确设置 basename 参数。

第三方包

以下第三方软件包也可用。

DRF 嵌套路由

drf-nested-routers 软件包 提供了路由器和关系字段,用于处理嵌套资源。

ModelRouter (wq.db.rest)

wq.db 软件包 提供了一个高级 ModelRouter 类(和单例实例),该类使用 register_model() API 扩展了 DefaultRouter。与 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 扩展

DRF-extensions 软件包 提供了 路由器,用于创建 嵌套视图集集合级别控制器,其中包含 可自定义的端点名称