Django REST Framework combining routers from different apps

前端 未结 6 1851
面向向阳花
面向向阳花 2020-12-12 14:33

I have a project that spans multiple apps:

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

Each app has a router for Django REST Framework to i

相关标签:
6条回答
  • 2020-12-12 14:49

    I ended up creating a single URLs file that contains all the routes I want at 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')
    

    As a side effect, this allowed me to get rid of all the individual urls.py files in each app, which you would normally want but in this case the entire collection of apps needs a unified URL structure and so removal is more sensible.

    I then reference it from urls.py:

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

    Now if I ever want to change routes for v2, I can just include a v2 URLs file as well and eventually deprecate the v1 file.

    0 讨论(0)
  • 2020-12-12 14:52

    Another solution is to use SimpleRouter to define routers for individual apps. Then, use a customized DefaultRouter to include app specific routes. This way all of the app specific url definitions will stay in the corresponding app.

    Lets say you have two apps named "app1" and "app2" each of these apps have a directory named "api" and in this directory there is a file named "urls" that contain all your route definitions.

    ├── project/ │ ├── api_urls.py │ ├── app1 │ │ ├── api │ │ │ ├── urls.py │ ├── app2 │ │ ├── api │ │ │ ├── urls.py │ ├── patches │ │ ├── routers.py

    use patches/router.py to define a class named DefaultRouter that inherits from 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)
    

    Fill your api urls with route definitions like

    """
    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)
    
    0 讨论(0)
  • 2020-12-12 14:54

    If you are implementing a SimpleRouter you just concatenate its urls with the urlpatterns list

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

    On the main urls.py file

    from di_apiary_integration.urls import router as di_integration_routers
    

    To register the URL's you can do:

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

    or

    urlpatterns += di_integ_router.urls
    

    Both will work!

    Important

    ImportProject needs to be either a ModelViewSet or a ViewSet, if you create this as simple APIView you will need to register that as normal CBV View using as_view()

    0 讨论(0)
  • 2020-12-12 15:01

    This gets all the ViewSet routes listed on the base API URL.

    It defines the routes as a list in the respective included app.urls so they can be registered elsewhere.

    After including them in the base urls.py, the nested list of lists is built and looped through to register all routes at the same level in the 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])
    

    Results:

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

    This also has less repetition in each file, and arguably makes it easier to read.

    Also, it remains completely decoupled.

    If the included app is used elsewhere, the same method can be used internally to register it's own routes without being included anywhere.

    Just drop the outside loop

    routeList = (
        (r'bars', BarViewSet),
        (r'bazs', BazViewSet),
    )
    
    router = routers.DefaultRouter()
    for route in routeList:
        router.register(route[0], route[1])
    
    0 讨论(0)
  • 2020-12-12 15:01

    You can use router.registry.extend(app_router.registry), total example:

    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)),
    ]
    
    0 讨论(0)
  • 2020-12-12 15:05

    @Colton Hicks comment

    Let's say we have 2 apps (permissions, users) inside "apps folder". Then we can do something like this:

    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)
    
    0 讨论(0)
提交回复
热议问题