web-dev-qa-db-fra.com

Django REST Framework combinant des routeurs de différentes applications

J'ai un projet qui couvre plusieurs applications:

./project/app1
./project/app2
./project/...

Chaque application possède un routeur pour que Django REST Framework incorpore les parties de l'API fournies par cette application:

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

Étant donné que les applications sont séparées, mon fichier d’URL de niveau supérieur (./project/urls.py) inclut chacun des fichiers d’URL des applications distinctes:

url(r'^api/app1/', include('app1.urls', namespace='a1')),
url(r'^api/app2/', include('app2.urls', namespace='a2')),

Cela signifie que Django REST Framework affiche une racine d'API distincte pour chaque application. Ce que je voudrais, cependant, c'est une structure d'API unifiée. Ainsi, si je navigue vers http://example.com/api/, je vois la liste complète de toutes les URL disponibles à ce niveau de la hiérarchie.

Je suppose qu'il existe un moyen d'inclure tous les routeurs distincts définis dans les fichiers urls.py de chaque application dans un seul routeur, mais je ne trouve pas de documentation sur la procédure à suivre. Est-ce que je manque quelque chose d'évident?

43
seawolf

Une autre solution consiste à utiliser SimpleRouter pour définir des routeurs pour des applications individuelles. Ensuite, utilisez une DefaultRouter personnalisée pour inclure les itinéraires spécifiques à une application. De cette façon, toutes les définitions d'URL spécifiques à l'application resteront dans l'application correspondante.

Disons que vous avez deux applications nommées "app1" et "app2", chacune de ces applications ayant un répertoire nommé "api" et dans ce répertoire se trouve un fichier nommé "urls" contenant toutes les définitions de votre route.

├── project/ │ ├── api_urls.py │ ├── app1 │ │ ├── api │ │ │ ├── urls.py │ ├── app2 │ │ ├── api │ │ │ ├── urls.py │ ├── patches │ │ ├── routers.py

utilisez patches/router.py pour définir une classe nommée DefaultRouter qui hérite de 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)

Remplissez vos urls api avec les définitions de route comme

"""
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)
29
Saleem Latif

Tous les itinéraires ViewSet répertoriés dans l’URL de l’API de base sont ainsi obtenus.

Il définit les itinéraires sous forme de liste dans les applications incluses incluses afin de pouvoir les enregistrer ailleurs.

Après les avoir inclus dans la base urls.py, la liste de listes imbriquées est construite et bouclée pour enregistrer toutes les routes au même niveau dans l'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])

Résultats:

{
    "foo": "http://localhost:8000/foos/",
    "bar": "http://localhost:8000/bars/",
    "baz": "http://localhost:8000/bazs/",
}

Cela a également moins de répétition dans chaque fichier et facilite sans doute la lecture.

En outre, il reste complètement découplé. 

Si l'application incluse est utilisée ailleurs, la même méthode peut être utilisée en interne pour enregistrer ses propres itinéraires sans être incluse nulle part.

Il suffit de laisser tomber la boucle extérieure

routeList = (
    (r'bars', BarViewSet),
    (r'bazs', BazViewSet),
)

router = routers.DefaultRouter()
for route in routeList:
    router.register(route[0], route[1])
12
shanemgrey

J'ai fini par créer un seul fichier d'URL contenant toutes les routes que je veux sur 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')

En tant qu'effet secondaire, cela m'a permis de me débarrasser de tous les fichiers individuels urls.py dans chaque application, ce que vous voudriez normalement, mais dans ce cas, l'ensemble de la collection d'applications nécessite une structure d'URL unifiée et sa suppression est donc plus raisonnable.

Je le référence ensuite à partir de urls.py:

import api_v1
urlpatterns = patterns('',
    ...,
    url(r'^api/v1/', include(api_v1, namespace='api-v1')),
)

Maintenant, si je veux changer de route pour la v2, je peux simplement inclure également un fichier d'URL v2 et éventuellement déconseiller le fichier v1.

6
seawolf

Si vous implémentez SimpleRouter, il vous suffit de concaténer ses URL avec la liste urlpatterns.

router = SimpleRouter()
router.register(r'import-project', ImportProject, base_name='di-integ')

Sur le fichier principal urls.py

from di_apiary_integration.urls import router as di_integration_routers

Pour enregistrer les URL, vous pouvez faire:

url(r'^di-integration/', include(di_integration_routers.urls)),

ou 

urlpatterns += di_integ_router.urls

Les deux vont marcher!

Important

ImportProject doit être un ModelViewSet ou un ViewSet. Si vous créez ceci en tant que simple APIView, vous devez l'enregistrer en tant que vue CBV normale en utilisant as_view ().

0
Gregory