I have a project that spans multiple apps:
./project/app1
./project/app2
./project/...
Each app has a router for Django REST Framework to incorporate the parts of the API provided by that app:
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)),
]
Because the apps are separate, my top-level URLs file (./project/urls.py
) includes each of the URLs files from the separate apps:
url(r'^api/app1/', include('app1.urls', namespace='a1')),
url(r'^api/app2/', include('app2.urls', namespace='a2')),
This means that Django REST Framework shows a separate API root for each app. What I would like, however, is a unified API structure, so that if I navigate to http://example.com/api/
I see the full list of all URLs that are available at that level of the hierarchy.
I presume there is a way to include all my separate routers defined in the individual urls.py
files for each app into a single router, but I can't find documentation on how to do this. Am I missing something obvious?
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
Results:
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
I ended up creating a single URLs file that contains all the routes I want at urls_api_v1.py:
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
: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.
If you are implementing a SimpleRouter you just concatenate its urls with the urlpatterns list
On the main urls.py file
To register the URL's you can do:
or
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()
Another solution is to use
SimpleRouter
to define routers for individual apps. Then, use a customizedDefaultRouter
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 namedDefaultRouter
that inherits fromrest_framework.routers.DefaultRouter
.Fill your api urls with route definitions like