Django REST Framework结合了来自不同应用程序的路由器

sea*_*olf 51 django django-rest-framework

我有一个跨多个应用程序的项目:

./project/app1
./project/app2
./project/...
Run Code Online (Sandbox Code Playgroud)

每个应用程序都有一个Django REST Framework路由器,可以整合该应用程序提供的API部分:

from django.conf.urls import url, include
from rest_framework.routers import DefaultRouter
from .views import ThingViewSet

router = DefaultRouter()
router.register(r'things', ThingViewSet, base_name='thing')

urlpatterns = [
    url(r'^', include(router.urls)),
]
Run Code Online (Sandbox Code Playgroud)

由于应用是独立的,因此我的顶级网址file(./project/urls.py)包含来自不同应用的每个网址文件:

url(r'^api/app1/', include('app1.urls', namespace='a1')),
url(r'^api/app2/', include('app2.urls', namespace='a2')),
Run Code Online (Sandbox Code Playgroud)

这意味着Django REST Framework为每个应用程序显示一个单独的API根目录.但是,我想要的是一个统一的API结构,因此,如果我导航到http://example.com/api/我,我会看到该层次结构中所有可用URL的完整列表.

我认为有一种方法可以将urls.py每个应用程序的单个文件中定义的所有单独路由器包含在一个路由器中,但是我找不到有关如何执行此操作的文档.我错过了一些明显的东西吗

小智 39

另一种解决方案是SimpleRouter用于为各个应用定义路由器.然后,使用自定义DefaultRouter包含应用程序特定路由.这样,所有特定于应用程序的URL定义都将保留在相应的应用程序中.

假设您有两个名为"app1"和"app2"的应用程序,每个应用程序都有一个名为"api"的目录,并且在此目录中有一个名为"urls"的文件,其中包含您的所有路径定义.

??? project/ ? ??? api_urls.py ? ??? app1 ? ? ??? api ? ? ? ??? urls.py ? ??? app2 ? ? ??? api ? ? ? ??? urls.py ? ??? patches ? ? ??? routers.py

用于patches/router.py定义名称DefaultRouter继承自的类rest_framework.routers.DefaultRouter.

from rest_framework import routers

class DefaultRouter(routers.DefaultRouter):
    """
    Extends `DefaultRouter` class to add a method for extending url routes from another router.
    """
    def extend(self, router):
        """
        Extend the routes with url routes of the passed in router.

        Args:
             router: SimpleRouter instance containing route definitions.
        """
        self.registry.extend(router.registry)
Run Code Online (Sandbox Code Playgroud)

用路线定义填写你的api url

"""
URL definitions for the api.
"""
from patches import routers

from app1.api.urls import router as app1_router
from app2.api.urls import router as app2_router

router = routers.DefaultRouter()
router.extend(app1_router)
router.extend(app2_router)
Run Code Online (Sandbox Code Playgroud)

  • 或者甚至更简单,您只需从app1.urls导入路由器作为app1router`导入特定应用程序的路由器,然后在主urls.py文件中使用默认路由器注册此路由器`router.registry.extend(app1router.registry) `然后你不必继承任何东西. (38认同)
  • @ColtonHicks +1评论.工作得很好. (3认同)
  • 确实。好评论。无需子类化。 (2认同)

sha*_*rey 15

这将获取基本API URL上列出的所有ViewSet路由.

它将路由定义为相应包含的app.urls中的列表,以便可以在其他地方注册.

将它们包含在基本urls.py中之后,构建嵌套的列表列表并循环遍历以在API中注册同一级别的所有路由

# foo.urls
routeList = (
    (r'foos', FooViewSet),
)

# barBaz.urls
routeList = (
    (r'bars', BarViewSet),
    (r'bazs', BazViewSet),
)

# urls
from rest_framework import routers
from foo import urls as fooUrls
from barBaz import urls as barBazUrls

routeLists = [
    fooUrls.routeList,
    barBazUrls.routeList,
]

router = routers.DefaultRouter()
for routeList in routeLists:
    for route in routeList:
        router.register(route[0], route[1])
Run Code Online (Sandbox Code Playgroud)

结果:

{
    "foo": "http://localhost:8000/foos/",
    "bar": "http://localhost:8000/bars/",
    "baz": "http://localhost:8000/bazs/",
}
Run Code Online (Sandbox Code Playgroud)

这也减少了每个文件的重复次数,并且可以说更容易阅读.

此外,它仍然完全解耦.

如果在其他地方使用了包含的app,则可以在内部使用相同的方法来注册它自己的路由,而不必包含在任何地方.

只需放下外部循环

routeList = (
    (r'bars', BarViewSet),
    (r'bazs', BazViewSet),
)

router = routers.DefaultRouter()
for route in routeList:
    router.register(route[0], route[1])
Run Code Online (Sandbox Code Playgroud)


Voi*_*lin 7

@科尔顿·希克斯评论

假设“apps 文件夹”中有 2 个应用程序(权限、用户)。然后我们可以这样做:

from apps.users.api.urls import router as users_router
from apps.permissions.api.urls import router as permissions_router


router = DefaultRouter()
router.registry.extend(users_router.registry)
router.registry.extend(permissions_router.registry)
Run Code Online (Sandbox Code Playgroud)


sea*_*olf 6

我最终创建了一个URL文件,其中包含urls_api_v1.py所需的所有路由:

router = DefaultRouter()
router.register(r'app1/foos', FooViewSet, base_name='foo')
router.register(r'app2/bars', BarViewSet, base_name='bar')
router.register(r'app2/bazs', BazViewSet, base_name='baz')
Run Code Online (Sandbox Code Playgroud)

作为一个副作用,这使我能够摆脱每个应用程序中的所有个人urls.py文件,这些文件通常是您需要的,但在这种情况下,整个应用程序集合需要统一的URL结构,因此删除更为明智.

然后我引用它urls.py:

import api_v1
urlpatterns = patterns('',
    ...,
    url(r'^api/v1/', include(api_v1, namespace='api-v1')),
)
Run Code Online (Sandbox Code Playgroud)

现在,如果我想更改v2的路由,我也可以包含一个v2 URL文件,并最终弃用v1文件.

  • 但是你的应用程序是自包含的,所以他们应该拥有自己的urls.py. 这也是我的情况,我想避免将所有网址转移到集中网址,这没有任何意义.如何统一网址,同时保持应用程序分离?当然有一种方法可以包含来自不同应用程序的已有urls.py吗? (7认同)
  • 我非常不同意:API组织是每个应用程序的责任,顶级应用程序不需要了解每个应用程序的URL树。这就是 API 可发现性(和 api_root)的意义所在。顶级项目只会在它选择的顶级端点中插入它认为合适的不同子 API。然后一切都应该可以正常工作,包括从 api 根目录开始浏览整个 API 的选项。 (3认同)

joe*_*euk 6

您可以使用router.registry.extend(app_router.registry),总示例:

from django.urls import path, include
from rest_framework import routers
from app1.rest import router as app1_router
from app2.rest import router as app2_router

router = routers.DefaultRouter()
router.registry.extend(app1_router.registry)
router.registry.extend(app2_router.registry)

urlpatterns = [
    path('', include(router.urls)),
]
Run Code Online (Sandbox Code Playgroud)