You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
注册

DRF可浏览API中不显示已引入的应用URL

DRF可浏览API中不显示已引入的应用URL

我明白你遇到的问题了——把路由拆分到各个app的urls.py后,API能正常访问,但DRF的可浏览API根页面就是不显示这些拆分出去的路由,不过drf-spectacular却能正确识别。这其实是DRF可浏览API的一个设计特点,不是bug,咱们一步步来解决。

为什么会出现这个情况?

DRF的可浏览API根页面是由你在根urls.py里初始化的DefaultRouter生成的,这个根路由器只会展示自己直接注册的ViewSet路由。而你拆分到een app里的是另一个独立的DefaultRouter,通过include引入后,它的路由属于单独的分支,根路由器根本“不知道”这些路由的存在,自然不会在根页面展示它们。

至于drf-spectacular能识别,是因为它会遍历整个项目的urlpatterns来收集API信息,不管路由属于哪个路由器,所以不受这个限制。

解决办法

这里有两种常用方案,你可以根据自己的项目需求选择:

方案一:将子app的路由器合并到主路由器(推荐)

这种方法能让根路由器自动包含所有app的路由,可浏览API根页面也会自动展示所有链接,无需手动维护。

修改你的根urls.py

# urls.py
from django.contrib import admin
from django.urls import include, path
from rest_framework.routers import DefaultRouter
# 导入子app的路由器
from een.urls import router as een_router

import core.views

router = DefaultRouter()

# 注册core模块的视图集
router.register(r'core/settings', core.views.SettingsViewSet, basename='settings')
router.register(r'core/organization', core.views.OrgViewSet, basename='org')

# 合并een模块的路由器注册项
router.registry.extend(een_router.registry)

urlpatterns = [
    path('api/', include(router.urls)),
    # 移除原来的 path('api/een/', include('een.urls')),因为已经合并了
    path('admin/', admin.site.urls),
    path('', include('rest_framework.urls', namespace='rest_framework')),
    path('api/tokenauth/', authviews.obtain_auth_token),
]

这样操作后,主路由器就包含了所有ViewSet的注册信息,访问/api/时就能看到所有路由了。

方案二:自定义API根视图(适合完全保持路由拆分的场景)

如果你不想合并路由器,希望每个app的路由完全独立,可以自定义一个根视图,手动列出所有API入口:

修改根urls.py

from django.contrib import admin
from django.urls import include, path
from rest_framework.routers import DefaultRouter
from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework.reverse import reverse

import core.views

router = DefaultRouter()
router.register(r'core/settings', core.views.SettingsViewSet, basename='settings')
router.register(r'core/organization', core.views.OrgViewSet, basename='org')

# 自定义根视图
@api_view(['GET'])
def api_root(request, format=None):
    return Response({
        # core模块的路由
        'core/settings': reverse('settings-list', request=request, format=format),
        'core/organization': reverse('org-list', request=request, format=format),
        # een模块的路由,注意要使用app_name的命名空间
        'een/cvs': reverse('een:een-cvs-list', request=request, format=format),
        # 后续新增路由都需要在这里手动添加
    })

urlpatterns = [
    path('api/', api_root),  # 把自定义根视图放在最前面
    path('api/', include(router.urls)),
    path('api/een/', include('een.urls')),
    path('admin/', admin.site.urls),
    path('', include('rest_framework.urls', namespace='rest_framework')),
    path('api/tokenauth/', authviews.obtain_auth_token),
]

这种方式的缺点是需要手动维护根视图里的链接,新增路由时要记得更新,但好处是能完全保持各app路由的独立性。

补充说明

如果你保留原来的include方式,其实访问/api/een/是能看到该app下的路由列表的——这是子app里的DefaultRouter生成的页面,但根页面/api/只会展示主路由器的内容。

备注:内容来源于stack exchange,提问作者normic

火山引擎 最新活动