You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django DRF嵌套ViewSet路由注册访问异常解决方案

问题根源

DRF内置DefaultRouter/SimpleRouter的路由匹配逻辑按注册顺序优先匹配前缀,你当前代码先注册了前缀为journal的路由,后续访问/journal/win这类路径时,路由器会先匹配到journal前缀,把路径剩余的win当做JournalViewSet详情接口的主键参数,根本不会走到后续注册的WinLogViewSet等视图集,自然会出现返回journal接口Schema、POST方法不被允许的问题。
当你把前缀改成非嵌套的move、win这类和journal平级的路径时,前缀没有包含关系,不会出现匹配错位,功能就正常。

解决方案

根据实际业务场景选对应方案即可:

  • 场景1:子路径不需要依赖父资源ID(即当前场景,/journal/win这类接口不需要接收journal主键参数,只是路径前缀统一挂在journal下)
    两种实现方式:

    1. 调整注册顺序,把所有前缀更长的子路由放在短前缀路由前面注册,路由器会优先匹配到更长的前缀,修改后的代码如下:
    from rest_framework import routers
    
    from .viewsets import (JournalViewSet, StatisticViewSet, FuelLogViewSet,
                          MoveLogViewSet, RestLogViewSet, WinLogViewSet,
                          GreatfulLogViewSet)
    
    
    router = routers.DefaultRouter()
    # 优先注册所有带journal/前缀的长路径路由
    router.register(r'journal/statistic', StatisticViewSet)
    router.register(r'journal/fuel', FuelLogViewSet)
    router.register(r'journal/move', MoveLogViewSet)
    router.register(r'journal/rest', RestLogViewSet)
    router.register(r'journal/win', WinLogViewSet)
    router.register(r'journal/greatful', GreatfulLogViewSet)
    # 最后注册前缀最短的journal根路由
    router.register(r'journal', JournalViewSet)
    
    urlpatterns = router.urls
    
    1. 自定义路由器,自动按前缀长度倒序匹配,不需要手动调整注册顺序,一劳永逸:
    from rest_framework import routers
    
    class LongPrefixFirstRouter(routers.DefaultRouter):
        def register(self, prefix, viewset, basename=None):
            super().register(prefix, viewset, basename)
            # 每次注册后按前缀长度从长到短排序,保证长前缀优先匹配
            self.registry.sort(key=lambda item: -len(item[0]))
    

    用这个LongPrefixFirstRouter替换原来的DefaultRouter即可,注册顺序不影响匹配结果。

  • 场景2:需要真正的资源嵌套(比如路径为/journal/<journal_id>/win/,接口需要拿到父级journal的ID过滤对应数据)
    这种场景不要手动拼接路径前缀,使用嵌套路由写法即可:

    from rest_framework import routers
    from .viewsets import (JournalViewSet, WinLogViewSet) # 其他视图集省略
    
    router = routers.DefaultRouter()
    router.register(r'journal', JournalViewSet, basename='journal')
    
    # 生成嵌套路由 /journal/{journal_pk}/win/
    journal_nested_router = routers.NestedSimpleRouter(router, r'journal', lookup='journal')
    journal_nested_router.register(r'win', WinLogViewSet, basename='journal-win')
    # 其余fuel、move等子资源按相同方式注册到journal_nested_router即可
    
    urlpatterns = router.urls + journal_nested_router.urls
    

    这种写法下,在子资源的ViewSet中可以通过self.kwargs['journal_pk']获取路径中传入的父资源ID,做对应的数据过滤、权限校验逻辑。

内容的提问来源于stack exchange,提问作者Dmitriy Lunev

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.31 13:15:47