在使用 Django REST Framework (DRF) 开发 API 的时候,我们常常会看到这样的代码:
python
from rest_framework.routers import DefaultRouter
from .views import UserViewSet, GroupViewSet
router = DefaultRouter()
router.register(r'users', UserViewSet)
router.register(r'groups', GroupViewSet)
urlpatterns = router.urls
只需要几行代码,就能自动生成 users/ 和 groups/ 的路由。
那么问题来了:
-
Router.register() 做了什么?
-
为什么只需要写 ViewSet 就能生成一堆路由?
-
router.urls 是怎么来的?
本文将深入解析 DRF Router 的注册流程。
1. Router 的作用
在 Django 原生路由中,我们需要手动写 URL:
python
urlpatterns = [
path('users/', UserList.as_view()),
path('users/<int:pk>/', UserDetail.as_view()),
]
但是在 DRF 中,一个 ViewSet 通常包含了 list/create/retrieve/update/destroy 等多个动作。
如果手写 URL 映射会非常繁琐。
于是,Router 的职责就是:根据 ViewSet 自动生成标准化的 URLConf。
2. Router.register() 做了什么?
以 DefaultRouter 为例,调用 register() 时:
python
router.register(r'users', UserViewSet)
主要做了三件事:
-
保存注册信息
把
prefix='users'和viewset=UserViewSet存入self.registry列表中。pythonself.registry.append((prefix, viewset, basename)) -
确定 basename
-
如果没指定,会自动从
UserViewSet.queryset.model推断模型名称(如user)。 -
这个
basename决定了路由命名空间,例如:user-list、user-detail。
-
-
延迟生成 URL
register()并不会立刻生成 URL,而是等待访问router.urls时再统一构建。
3. router.urls 是怎么生成的?
当我们访问 router.urls 时,Router 会执行以下流程:
-
遍历
self.registry中的所有注册项每个项包含:
prefix、viewset、basename -
调用
get_urls()根据
ViewSet的动作方法(list、create、retrieve、update、partial_update、destroy)生成对应的路由规则。例如
UserViewSet默认生成:URL HTTP方法 动作 /users/GET list /users/POST create /users/{pk}/GET retrieve /users/{pk}/PUT update /users/{pk}/PATCH partial_update /users/{pk}/DELETE destroy -
拼接为最终的
urlpatterns这就是
router.urls的来源,最终交给 Django 的 URLConf 系统使用。
4. DefaultRouter 和 SimpleRouter 的区别
-
SimpleRouter
只生成 CRUD 的基础路由。
-
DefaultRouter
除了 CRUD 路由,还会额外生成一个 根 API 页面 (
api-root/),列出所有注册的资源入口。
示例:
访问 /api/,会看到:
python
{
"users": "http://127.0.0.1:8000/api/users/",
"groups": "http://127.0.0.1:8000/api/groups/"
}
5. 自定义 Router
如果你需要定制路由规则,可以继承 SimpleRouter 并重写 routes 属性:
python
from rest_framework.routers import SimpleRouter, Route
class CustomRouter(SimpleRouter):
routes = [
Route(
url=r'^{prefix}/custom/{lookup}{trailing_slash}$',
mapping={'get': 'custom_action'},
name='{basename}-custom',
detail=True,
initkwargs={'suffix': 'Custom'}
),
]
这样就可以为所有 ViewSet 自动添加一个自定义路由。
6. 总结
-
register() 负责登记
prefix、viewset、basename,并保存到self.registry。 -
router.urls 在第一次访问时动态生成 URLConf,映射到
ViewSet的标准动作。 -
DefaultRouter 比
SimpleRouter多了一个根 API 入口。 -
Router 本质上就是一个 URL 自动生成器,大大简化了手写路由的工作。