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

在...创建的问题 Fri, Jul 17, 2015 12:00 AM

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

 
./project/app1
./project/app2
./project/...

每个应用都有一个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)),
]

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

 
url(r'^api/app1/', include('app1.urls', namespace='a1')),
url(r'^api/app2/', include('app2.urls', namespace='a2')),

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

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

    
47
4个答案                              4 跨度>                         

另一种解决方案是使用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)

使用

等路线定义填写您的API  
"""
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)
    
36
2016-12-01 06:48:28Z
  1. 或者更简单一点,您只需为特定应用程序from app1.urls import router as app1router导入路由器,然后在主urls.py文件router.registry.extend(app1router.registry)中使用默认路由器注册此路由器,然后您就不要
    必须是子类。
2018-01-16 21:21:40Z
  • @ ColtonHicks +1评论。完美无缺。
    2018-03-31 16:07:34Z
  • 确实。好评。不需要子类。
    2018-05-31 18:42:48Z
  • 推荐此评论的解决方案。
    2019-04-12 18:59:16Z
  • 醇>

    这将获取基本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])
    

    结果:

     
    {
        "foo": "http://localhost:8000/foos/",
        "bar": "http://localhost:8000/bars/",
        "baz": "http://localhost:8000/bazs/",
    }
    

    每个文件的重复次数也较少,可以说更容易阅读。

    此外,它仍然完全解耦。

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

    放弃外部循环

     
    routeList = (
        (r'bars', BarViewSet),
        (r'bazs', BazViewSet),
    )
    
    router = routers.DefaultRouter()
    for route in routeList:
        router.register(route[0], route[1])
    
        
    14
    2016-09-20 15:53:40Z
    1. 优秀的想法。这里唯一的改进空间是我认为可能存在名称冲突(例如,如果两个应用程序试图注册'bars'名称)。但是从一个起点来看,这是一个很好的方法。谢谢!
      2016-10-15 23:40:13Z
    2. 这是真的。将是一个简单的改变。您可以将其命名为(r'foos', FooViewSet),,而不是路由为(r'foo/route1', FooViewSet),。只要您的应用名称是唯一的,就不应该发生冲突。
      2018-04-11 14:51:54Z
    3. 醇>

    我最终在urls_api_v1.py中创建了一个包含我想要的所有路由的URL文件:

     
    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')
    

    作为一个副作用,这让我可以摆脱每个应用程序中你通常想要的所有个人urls.py文件,但在这种情况下,整个应用程序集合需要统一的URL结构,因此删除更多明智的。

    然后我从urls.py引用它:

     
    import api_v1
    urlpatterns = patterns('',
        ...,
        url(r'^api/v1/', include(api_v1, namespace='api-v1')),
    )
    

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

        
    6
    2015-07-22 23:18:30Z
    1. 但是你的应用程序是自包含的,因此它们应该有自己的urls.py.这也是我的情况,我想避免将所有网址移动到集中网址,这没有任何意义。如何统一网址,同时保持应用程序分离?当然有一种方法可以包含来自不同应用程序的现有urls.py吗?
      2016-02-08 19:07:15Z
    2. 我还没有找到这样的方式,以为我停止了照顾我想出了这么多。我认为这是一种合理的方法,因为 apps 是分开的,但API不是。通过让REST Framework在逐个应用的基础上找出URL结构,我所做的就是将它们组合成一个不特定于任何应用程序的单一顶级安排。这与顶级urls.py没有太大区别,它将每个单独的应用程序的urls.py文件汇集到它们自己的命名空间中。
      2016-02-09 15:48:52Z
    3. 我恭敬地不同意:API组织负责每个应用程序,顶级应用程序不需要了解每个应用程序的URL树。这就是API可发现性(和api_root)的关键所在。顶级项目将在其选择的顶级端点中插入其认为合适的不同子API。然后一切都应该工作,包括从api root开始浏览整个API的选项。
      2016-02-09 16:05:47Z
    4. 我看到你在说什么,但我不认为这是普遍真实的。例如,对于我的情况,我有多个松散耦合的应用程序。有些客户只在一个区域或另一个区域花费时间,但有些客户出于必要而跨越边界。从开发的角度来看,这些应用程序是独立的,或者至多是松散耦合的;这有助于更轻松地开发,测试和管理数据。每个应用程序都提供少量的ViewSet,而单个urls.py文件将整个代码库捆绑到一个客户端可见的API中。
      2016-02-10 00:11:58Z
    5. 如果它们是真正独立的应用程序,那么我对于在API中拥有真正独立的视图毫不犹豫,每个应用程序都有一个。这可以通过简单地以传统方式为每个应用程序导入urls.py来处理。
      2016-02-10 00:13:23Z
    6. 醇>

    如果您正在实现SimpleRouter,您只需将其url与urlpatterns列表连接

     
    router = SimpleRouter()
    router.register(r'import-project', ImportProject, base_name='di-integ')
    

    在主urls.py文件

    上  
    from di_apiary_integration.urls import router as di_integration_routers
    

    要注册URL,您可以这样做:

     
    url(r'^di-integration/', include(di_integration_routers.urls)),
    

     
    urlpatterns += di_integ_router.urls
    

    两者都有效!

    重要

    ImportProject需要是ModelViewSet或ViewSet,如果您将其创建为简单的APIView,则需要使用as_view()将其注册为普通的CBV视图

        
    0
    2018-04-13 10:14:15Z
    来源放置 这里