16👍
To get all apps in the same API root, you need to register all your apps with the same DefaultRouter.
One way to achieve this is to make a custom router, which intercepts the register call and propagates it to a shared router. You then use this shared router to get the api urls.
class SharedAPIRootRouter(SimpleRouter):
shared_router = DefaultRouter()
def register(self, *args, **kwargs):
self.shared_router.register(*args, **kwargs)
super().register(*args, **kwargs)
# if not py3: super(SharedAPIRootRouter, self).register(*args,**kwargs)
Then in each app:
# in app1/urls.py
router = SharedAPIRootRouter()
router.register(r'app1', App1ModelViewSet)
# in app2/urls.py
router = SharedAPIRootRouter()
router.register(r'app2', App2ModelViewSet)
In your main urls.py, you must ensure you import the app urls so that registration occurs before we ask for shared_router.urls
import app1.urls
import app2.urls
def api_urls():
return SharedAPIRootRouter.shared_router.urls
urlpatterns = patterns(
'',
url(r'^api/', include(api_urls())),
)
if you do not want to import the urls explicitly, you can do it by convention:
def api_urls():
from importlib import import_module
for app in settings.INSTALLED_APPS:
try:
import_module(app + '.urls')
except (ImportError, AttributeError):
pass
return SharedAPIRootRouter.shared_router.urls
7👍
This is possible by passing around a single router instance as follows.
Create a file called router.py
or similar in your main
project folder:
from rest_framework import routers
common_router = routers.DefaultRouter()
In each app’s urls.py
put:
from main.router import common_router as router
router.register(r'myapp-model-name', MyAppViewSet)
In your main
urls.py
put:
import my_app1.urls # to register urls with router
import my_app2.urls # to register urls with router
...
# finally import router that includes all routes
from main.router import common_router
urlpatterns = [
...
url(r'^api/', include(common_router.urls)),
...
]
- Django: modifying/extending 3rd party apps
- In Django, how can I prevent a "Save with update_fields did not affect any rows." error?
- No module named backends.default.urls
- Using ModelFormMixin without the 'fields' attribute is prohibited
5👍
Both options are possible. You can either expose the router
or the urls
in each app, and merge those into your global urls
. I usually prefer using urls
(option 2) because it gives more flexibility in each app: you can define extra non-api URLs as needed.
Option 1
In your global urls.py:
from app1.api.routers import router1
from app2.api.routers import router2
urlpatterns = patterns('',
url(r'^snippets/', include('snippets.urls', namespace="snippets"))
...
url(r'^app1/api/', include(router1.urls)),
url(r'^app2/api/', include(router2.urls)),
)
You can as easily use the same endpoint for both routers (as long as you’re careful not to use conflicting routes):
urlpatterns = patterns('',
url(r'^snippets/', include('snippets.urls', namespace="snippets"))
...
url(r'^api/', include(router1.urls)),
url(r'^api/', include(router2.urls)),
)
Option 2
In appN/api/urls.py:
router = DefaultRouter()
router.register(r'users', views.UserViewSet)
router.register(include('app1.apis')
urlpatterns = patterns('',
url(r'^', include(router.urls)),
url(r'^misc/', some_other_view),
)
In your global urls.py:
urlpatterns = patterns('',
url(r'^snippets/', include('snippets.urls', namespace="snippets"))
...
url(r'^api/', include('app1.api.urls')),
url(r'^api/', include('app2.api.urls')),
)
Note that the urls
modules do not need to be the same as the urls
for standard views.
- Django: I get a [relation "auth_group" does not exist] error after syncdb
- How to resolve 'Import "django.contrib" could not be resolved from source' in VS Code?
- Django, division between two annotate result won't calculate correctly
0👍
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:
- You can control the registration of the apps in the main
urls.py
- The flexibility of
register(router)
allows you to register to different routers, for example when using both v1 and v2 for versioning.
- Why __unicode__ doesn't work but __str__ does?
- Annotate django query if filtered row exists in second table
- What am I not doing right in this django file upload form?
- Django: What is the most ideal place to store project-specific middleware?
- How do I make a custom model Field call to_python when the field is accessed immediately after initialization (not loaded from DB) in Django >=1.10?