Регистрация API в приложениях

С django-rest-framework я использую DefaultRouter

Я хочу предоставить API-интерфейсы нескольким приложениям, поэтому мой вопрос: могу ли я сделать это с помощью django и разместить регистрацию маршрутизаторов в каждом URL-адресе приложения и показать их как один агрегированный api или идеально в пространстве имен.

Другими словами, если app1 содержит modelA и modelB, а app2 содержит modelC:

  • Могу ли я объявить 2 маршрутизатора, которые появляются в mysite/app1/api и mysite/app2/api, или
  • Могу ли я иметь один api в mysite/api, который перечисляет все три модели, но регистрирует отдельные модели в своем собственном приложении urls.py

Что-то вроде

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

В качестве альтернативы есть простой способ, с помощью которого моя переменная маршрутизатора может быть доступна в каждом URL-адресе приложения, чтобы они могли вызывать router.register? Я не уверен, что

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

действительно вызывает выполнение кода в app1/urls.py в этой точке, чтобы он мог вызвать router.register каким-то образом, так что окончательный URL-адрес включает в себя все регистрации приложений, а также один проект.

UPDATE

Используя вариацию на Nicolas Cortot option 2, я получаю свой конкретный ресурс API для работы, но он не указан в качестве доступного ресурса в root API в myserver\api\

Я предполагаю, что каким-то образом DefaultRouter создает собственное определение страницы, а router.register добавляет к нему записи. Моя текущая настройка (и я думаю, что вариант Nicholas 1 также) создает два отдельных маршрутизатора, и только один может отображаться как корень сервера, с настройкой ниже, myserver\api\ перечисляет users, но не фрагменты.

Здесь моя текущая настройка:

проект 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')),
)

Проект/сниппеты/apiurls.py:

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

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

Если я отменил порядок записей в проекте urls.py как:

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

тогда я получаю snippets, но не users

Я думаю, Django выполняет первый сопоставленный маршрут.

Если кто-то не может сказать мне иначе, мне, похоже, нужна одна переменная маршрутизатора, которая должна быть передана и добавлена ​​к каким-то образом.

Ответ 1

Чтобы получить все приложения в одном корневом каталоге API, вам необходимо зарегистрировать все приложения с тем же DefaultRouter.

Одним из способов достижения этого является создание настраиваемого маршрутизатора, который перехватывает вызов регистра и передает его на общий маршрутизатор. Затем вы используете этот общий маршрутизатор для получения api-адресов.

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)

Затем в каждом приложении:

# in app1/urls.py 
router = SharedAPIRootRouter()
router.register(r'app1', App1ModelViewSet)

# in app2/urls.py
router = SharedAPIRootRouter()
router.register(r'app2', App2ModelViewSet)

В вашем основном urls.py вы должны убедиться, что вы импортируете URL-адреса приложения, чтобы регистрация происходила до того, как мы попросим 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())),   
)   

если вы не хотите явно импортировать URL-адреса, вы можете сделать это по соглашению:

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

Возможны оба варианта. Вы можете либо показать router, либо urls в каждом приложении и объединить их в свой глобальный urls. Обычно я предпочитаю использовать urls (вариант 2), потому что он дает большую гибкость в каждом приложении: вы можете определить дополнительные URL-адреса, отличные от api, по мере необходимости.

Вариант 1

В глобальном 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)),
)

Вы можете легко использовать одну и ту же конечную точку для обоих маршрутизаторов (если вы не используете конфликтующие маршруты):

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

Вариант 2

В 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),
)

В глобальном 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')),
)

Обратите внимание, что модули urls не обязательно должны быть такими же, как urls для стандартных представлений.

Ответ 3

Это возможно, передав один экземпляр маршрутизатора следующим образом.

Создайте файл с именем router.py или похожим в вашей основной папке проекта:

from rest_framework import routers
common_router = routers.DefaultRouter()

В каждом приложении urls.py put:

from main.router import common_router as router
router.register(r'myapp-model-name', MyAppViewSet) 

В главном 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)),
    ...
]