Django REST Framework, объединяющая маршрутизаторы из разных приложений
У меня есть проект, охватывающий несколько приложений:
./project/app1
./project/app2
./project/...
Каждое приложение имеет маршрутизатор для Django REST Framework для включения частей API, предоставляемых этим приложением:
from django.conf.urls import url, include
from rest_framework.routers import DefaultRouter
from .views import ThingViewSet
router = DefaultRouter()
router.register(r'things', ThingViewSet, base_name='thing')
urlpatterns = [
url(r'^', include(router.urls)),
]
Поскольку приложения являются отдельными, мой URL-адрес верхнего уровня (./project/urls.py
) включает в себя каждый из файлов URL-адресов из отдельных приложений:
url(r'^api/app1/', include('app1.urls', namespace='a1')),
url(r'^api/app2/', include('app2.urls', namespace='a2')),
Это означает, что Django REST Framework показывает отдельный корень API для каждого приложения. Однако я хотел бы создать единую структуру API, поэтому, если я перейду к http://example.com/api/
, я увижу полный список всех URL-адресов, доступных на этом уровне иерархии.
Я предполагаю, что есть способ включить все отдельные маршрутизаторы, определенные в отдельных файлах urls.py
для каждого приложения, в один маршрутизатор, но я не могу найти документацию о том, как это сделать. Мне что-то не хватает?
Ответы
Ответ 1
Другим решением является использование SimpleRouter
для определения маршрутизаторов для отдельных приложений. Затем используйте настраиваемый DefaultRouter
, чтобы включить в него конкретные маршруты. Таким образом, все определенные определения URL-адреса приложения останутся в соответствующем приложении.
Допустим, у вас есть два приложения с именем "app1" и "app2", в каждом из этих приложений есть каталог с именем "api", и в этом каталоге есть файл с именем "urls", который содержит все ваши определения маршрутов.
├── project/
│ ├── api_urls.py
│ ├── app1
│ │ ├── api
│ │ │ ├── urls.py
│ ├── app2
│ │ ├── api
│ │ │ ├── urls.py
│ ├── patches
│ │ ├── routers.py
используйте patches/router.py
, чтобы определить класс с именем DefaultRouter
, который наследуется от rest_framework.routers.DefaultRouter
.
from rest_framework import routers
class DefaultRouter(routers.DefaultRouter):
"""
Extends `DefaultRouter` class to add a method for extending url routes from another router.
"""
def extend(self, router):
"""
Extend the routes with url routes of the passed in router.
Args:
router: SimpleRouter instance containing route definitions.
"""
self.registry.extend(router.registry)
Заполните свои api url с помощью определений маршрутов, например
"""
URL definitions for the api.
"""
from patches import routers
from app1.api.urls import router as app1_router
from app2.api.urls import router as app2_router
router = routers.DefaultRouter()
router.extend(app1_router)
router.extend(app2_router)
Ответ 2
Получает все маршруты ViewSet, перечисленные в базовом URL-адресе API.
Он определяет маршруты как список в соответствующих включенных app.urls, чтобы они могли быть зарегистрированы в другом месте.
После включения их в базовый urls.py вложенный список списков создается и зацикливается, чтобы зарегистрировать все маршруты на одном уровне в API
# foo.urls
routeList = (
(r'foos', FooViewSet),
)
# barBaz.urls
routeList = (
(r'bars', BarViewSet),
(r'bazs', BazViewSet),
)
# urls
from rest_framework import routers
from foo import urls as fooUrls
from barBaz import urls as barBazUrls
routeLists = [
fooUrls.routeList,
barBazUrls.routeList,
]
router = routers.DefaultRouter()
for routeList in routeLists:
for route in routeList:
router.register(route[0], route[1])
Результаты:
{
"foo": "http://localhost:8000/foos/",
"bar": "http://localhost:8000/bars/",
"baz": "http://localhost:8000/bazs/",
}
Это также имеет меньше повторений в каждом файле и, возможно, облегчает чтение.
Кроме того, он остается полностью развязанным.
Если включенное приложение используется в другом месте, тот же метод можно использовать внутри, чтобы зарегистрировать собственные маршруты, не будучи включенными нигде.
Просто снимите внешний цикл
routeList = (
(r'bars', BarViewSet),
(r'bazs', BazViewSet),
)
router = routers.DefaultRouter()
for route in routeList:
router.register(route[0], route[1])
Ответ 3
В итоге я создал единый файл URL-адресов, содержащий все маршруты, которые я хочу найти по адресу urls_api_v1.py:
router = DefaultRouter()
router.register(r'app1/foos', FooViewSet, base_name='foo')
router.register(r'app2/bars', BarViewSet, base_name='bar')
router.register(r'app2/bazs', BazViewSet, base_name='baz')
В качестве побочного эффекта это позволило мне избавиться от всех отдельных файлов urls.py в каждом приложении, чего вы обычно хотели бы, но в этом случае для всей коллекции приложений требуется унифицированная структура URL, и поэтому удаление более разумно.
Затем я ссылаюсь на него с urls.py
:
import api_v1
urlpatterns = patterns('',
...,
url(r'^api/v1/', include(api_v1, namespace='api-v1')),
)
Теперь, если я когда-либо захочу изменить маршруты для v2, я могу просто добавить файл URL-адресов v2 и, в конечном счете, обесценить файл v1.