问题
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
:
- can I declare 2 routers that appear at
mysite/app1/api
andmysite/app2/api
, or - can I have a single api at
mysite/api
which lists all three models yet register the individual models in their own app'surls.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.
回答1:
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
回答2:
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.
回答3:
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)),
...
]
来源:https://stackoverflow.com/questions/20825029/registering-api-in-apps