Reputation: 1067
I have come across a problem regarding having the API apps seperate, while still being able to use the browsable API for navigation.
I have previously used a seperate routers.py
file in my main application containing the following extension of the DefaultRouter
.
class DefaultRouter(routers.DefaultRouter):
def extend(self, router):
self.registry.extend(router.registry)
Followed by adding the other application routers like this:
from . routers import DefaultRouter
from app1.urls import router as app1_router
# Default Router
mainAppRouter = DefaultRouter()
mainAppRouter.extend(app1_router)
where the app1_router
is a new SimpleRouter
object.
Now the problem occurs when I want to modify the SimpleRouter
and create my own App1Router
, such as this
class App1Router(SimpleRouter):
routes = [
Route(
url = r'^{prefix}{trailing_slash}$',
mapping = {
'get': 'retrieve',
'post': 'create',
'patch': 'partial_update',
},
name = '{basename}-user',
initkwargs = {}
),
]
This will not handle my extension correctly. As an example, GET
and PATCH
are not recognized as allowed methods whenever I extend the router, but when I dont extend, but only use the custom router, everything works fine.
My question is therefor, how can I handle extending custom routers across seperate applications, but still maintain a good browsable API?
Upvotes: 12
Views: 4497
Reputation: 96
The router registry is just a standard python list, so you can call YourRouter.registry.extend() directly on registy withouth the need to subclass the DefaultRouter, check the source of DRF here . Since registry is a list, when you extend registry you add another python list on top of python list, which implies calling YourRouter.registry.extend(app_router.registry). What worked for me, was importing routers from another apps (SimpleRouters) and adding them on top of default router registry.
#aplication_root/urls.py
from django.urls import include, path
from rest_framework.routers import DefaultRouter
from app_3.urls import router as app_3_router
router = DefaultRouter()
router.registry.extend(app_3_router.registry)
urlpatterns = [
path('api/', include(router.urls)),
]
If your are trying to add versioning to your app by adding the prefix, I suggest to take a look at versioning schema available in DRF - maybe it could fit your needs DRF versioning
for example with URLPathVersioning enabled in your settings
REST_FRAMEWORK = {
'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.URLPathVersioning'
}
urlparttern list from snipped above would look like that
urlpatterns = [
path('api/<version>/', include(router.urls)),
]
Upvotes: 1
Reputation: 141
I don't see this anywhere in your code:
where the app1_router is a new SimpleRouter object
actually your import says otherwise
from app1.urls import router as app1_router
I would change the App1Router to extend either app1.urls.router or DefaultRouter.
Upvotes: 0
Reputation: 1685
Have you tried leaving the default routes intact for SimpleRouter?
class App1Router(SimpleRouter):
def __init__(self):
super(SimpleRouter, self).__init__()
self.routes.insert(0, Route(
url = r'^{prefix}{trailing_slash}$',
mapping = {
'get': 'retrieve',
'post': 'create',
'patch': 'partial_update',
},
name = '{basename}-user',
initkwargs = {}
))
Upvotes: 1