Routers

资源路由允许你快速声明给定的有足够控制器的所有公共路由。而不是为你的index...声明单独的路由,一个强大的路由能在一行代码中声明它们。

— [Ruby on Rails 文档][cite]

某些Web框架(如Rails)提供了自动确定应用程序的URL应如何映射到处理传入请求的逻辑的功能。

REST框架添加了对自动URL路由到Django的支持,并为你提供了一种简单、快速和一致的方式来将视图逻辑连接到一组URL。

Usage

这里有一个简单的URL conf的例子,它使用 SimpleRouter

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 - 处理请求的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参数,因为无法从model名自动确定。


在路由中使用 include

路由器实例上的.urls属性只是一个URL模式的标准列表。对于如何添加这些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)),
]

路由器URL模式也支持命名空间的写法。

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'

在你不想使用为自定义操作生成的默认名称的情况下,ni可以使用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_path和url_name参数对自定义视图的URL生成进行额外的控制。

有关更多信息,请参阅viewset文档 [标记路由的额外操作][route-decorators].

API 向导

SimpleRouter

该路由器包括标准集合list, create, retrieve, update, partial_updatedestroy动作的路由。视图集中还可以使用@ detail_route@ list_route装饰器标记要被路由的其他方法。

URL 样式HTTP 方法动作URL 名
{prefix}/GETlist{basename}-list
POSTcreate
{prefix}/{methodname}/GET, or as specified by `methods` argument`@list_route` decorated method{basename}-{methodname}
{prefix}/{lookup}/GETretrieve{basename}-detail
PUTupdate
PATCHpartial_update
DELETEdestroy
{prefix}/{lookup}/{methodname}/GET, or as specified by `methods` argument`@detail_route` decorated method{basename}-{methodname}

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

router = SimpleRouter(trailing_slash=False)

尾部斜杠在Django中是常见的,但是在其他一些框架(如Rails)中默认不使用。你选择使用哪种风格在很大程度上是你个人偏好问题,虽然一些javascript框架可能需要一个特定的路由风格。

路由器将匹配包含除斜杠和句点字符以外的任何字符的查找值。对于更严格(或更宽松)的查找模式,请在视图集上设置lookup_value_regex属性。例如,你可以将查找限制为有效的UUID:

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

DefaultRouter

这个路由器类似于上面的SimpleRouter,但是还包括一个默认返回所有列表视图的超链接的API根视图。它还生成可选的.json样式格式后缀的路由。

URL 样式HTTP 方法动作URL 名称
[.format]GETautomatically generated root viewapi-root
{prefix}/[.format]GETlist{basename}-list
POSTcreate
{prefix}/{methodname}/[.format]GET, or as specified by `methods` argument`@list_route` decorated method{basename}-{methodname}
{prefix}/{lookup}/[.format]GETretrieve{basename}-detail
PUTupdate
PATCHpartial_update
DELETEdestroy
{prefix}/{lookup}/{methodname}/[.format]GET, or as specified by `methods` argument`@detail_route` decorated method{basename}-{methodname}

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

router = DefaultRouter(trailing_slash=False)

自定义 Routers

通常你并不需要实现自定义路由器,但如果你对API的网址结构有特定的要求,那它就十分有用了。这样做允许你以可重用的方式封装URL结构,确保你不必为每个新视图显式地编写URL模式。

实现自定义路由器的最简单的方法是继承一个现有的路由器类。.routes属性用于模板将被映射到每个视图集的URL模式。.routes属性是一个名为tuples的Route对象的列表。

Route命名元组的参数是:

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

  • {prefix} - 用于此组路由的URL前缀。
  • {lookup} - 用于与单个实例进行匹配的查找字段。
  • {trailing_slash} - 可以是一个'/'或一个空字符串,这取决于trailing_slash参数。

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

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

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

initkwargs: 实例化视图时应传递的任何其他参数的字典。注意,suffix参数被保留用于标识视图集类型,在生成视图名称和面包屑链接时使用。

自定义动态路由

你还可以定制@ list_route@detail_route装饰器的路由。要路由这些装饰器中的一个或两个,请在.routes列表中包含一个DynamicListRoute和/或DynamicDetailRoute命名的元组。

DynamicListRouteDynamicDetailRoute的参数是:

url: 表示要路由的URL的字符串。可以包括与“Route”相同的格式字符串,并且另外接受{methodname}{methodnamehyphen}格式字符串。

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

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

例子

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

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):
        """
        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 名称
/usersGETlistuser-list
/users/{username}GETretrieveuser-detail
/users/{username}/group-namesGETgroup_namesuser-group-names