Подтвердить что ты не робот

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 для каждого приложения, в один маршрутизатор, но я не могу найти документацию о том, как это сделать. Мне что-то не хватает?

4b9b3361

Ответ 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.