Registering API in apps

Viewed 3439

With django-rest-framework I'm using the DefaultRouter

I want to provide APIs to several apps, so my question is can I do this in a django manner and put my router registrations in each app URLconf and have them appear either as one aggregate api or ideally in a namespaced way.

In other words if app1 contains modelA and modelB, while app2 contains modelC:

  1. can I declare 2 routers that appear at mysite/app1/api and mysite/app2/api, or
  2. can I have a single api at mysite/api which lists all three models yet register the individual models in their own app's urls.py

Something like

router = DefaultRouter()
router.register(r'users', views.UserViewSet)
router.register(include('app1.apis')
router.register(include('app2.apis')

Alternatively is there a simple way in which my router variable can be made available in each app's URLconf so that they can call router.register? I'm not sure if

urlpatterns = patterns('',
url(r'^snippets/', include('snippets.urls', namespace="snippets"))
...
url(r'^api/', include(router.urls)),

actually cause the code in app1/urls.py to be executed at that point so that it could call router.register somehow, so that the final url call includes all the app registrations as well as the project one.

UPDATE

Using a variation on Nicolas Cortot's option 2 I get my specific resource API to work, but it is not listed as an available resource in the root API at myserver\api\

I assume that somehow DefaultRouter creates it's own page definition and router.register adds entries to it. My current setup (and I think Nicholas's option 1 as well) create two separate routers, and only one can get displayed as the server root, with the setup below, myserver\api\ lists users but not snippets.

Here's my current setup:

project urls.py:

router = DefaultRouter()
router.register(r'users', views.UserViewSet)

urlpatterns = patterns('',
    url(r'^admin/', include(admin.site.urls)),
    url(r'^api/', include(router.urls)),
    url(r'^api/', include('snippets.apiurls')),
    url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')),
)

project/snippets/apiurls.py:

router = DefaultRouter()
router.register(r'snippets', views.SnippetViewSet)

urlpatterns = patterns('',
    url(r'^', include(router.urls)),
)

If I reverse the order of the entries in the project urls.py as:

    url(r'^api/', include('snippets.apiurls')),
    url(r'^api/', include(router.urls)),

then I get snippets listed but not users

I guess Django is serving the first matching route.

Unless someone can tell me otherwise I seem to need a single router variable to be passed around and added to somehow.

4 Answers

As a more advanced variant on @Grischa, I like to extend his approach:

In the main's routers.py:

from rest_framework import routers

api_v1_router = routers.SimpleRouter()

In the main's urls.py:

from django.urls import include, path

import app1.urls
from .routers import api_v1_router

# Register app urls
app1.urls.register(api_v1_router)
app2.urls.register(api_v1_router)
...

urlpatterns = [
    ...
    path('v1/', include((api_v1_router.urls, 'v1'))),
    ...
]

In each app's urls.py:

from main.routers import api_v1_router
from .apis import MyAppViewSet1, MyAppViewSet2


def register(router):
    router.register(r'myapp-model-name1', MyAppViewSet1)
    router.register(r'myapp-model-name2', MyAppViewSet2)

Two advantages of this approach:

  1. You can control the registration of the apps in the main urls.py
  2. The flexibility of register(router) allows you to register to different routers, for example when using both v1 and v2 for versioning.
Related