2015-07-17 11 views
29

mam projekt, który obejmuje wiele aplikacji:Django odpoczynek ramy łączące routery z różnych aplikacji

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

Każda aplikacja posiada router dla Django odpoczynek ram włączenia części z API przez taki app:

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

Ponieważ aplikacje są oddzielne, mój plik URL najwyższego poziomu (./project/urls.py) obejmuje każdy z plików adresów URL z oddzielnych aplikacji:

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

Oznacza to, że struktura REST Django pokazuje oddzielny główny katalog API dla każdej aplikacji. Chciałbym jednak mieć ujednoliconą strukturę interfejsu API, aby po przejściu na stronę http://example.com/api/ zobaczyłem pełną listę wszystkich adresów URL dostępnych na tym poziomie hierarchii.

Zakładam, że istnieje sposób na uwzględnienie wszystkich oddzielnych routerów zdefiniowanych w poszczególnych plikach urls.py dla każdej aplikacji na jednym routerze, ale nie mogę znaleźć dokumentacji, jak to zrobić. Czy brakuje mi czegoś oczywistego?

Odpowiedz

19

Innym rozwiązaniem jest użycie SimpleRouter zdefiniować routery dla poszczególnych aplikacji.Następnie użyj dostosowanego DefaultRouter, aby uwzględnić trasy specyficzne dla aplikacji. W ten sposób wszystkie specyficzne dla aplikacji definicje adresów URL pozostaną w odpowiedniej aplikacji.

Powiedzmy, że masz dwie aplikacje o nazwie "app1" i "app2", każda z tych aplikacji ma katalog o nazwie "api", aw tym katalogu znajduje się plik o nazwie "adresy URL" zawierający wszystkie definicje tras.

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

użycie patches/router.py zdefiniować klasę o nazwie DefaultRouter która dziedziczy 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) 

Napełnij swoje adresy URL api z definicjami trasy jak

""" 
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) 
+0

Lub jeszcze prostszy możesz po prostu zaimportować swój router dla konkretnej aplikacji 'from app1.urls importuj router jako app1router', a następnie zarejestruj ten router za pomocą domyślnego routera w głównym pliku urls.py' router.registry.extend (app1router.registry) 'i wtedy nie musisz niczego podklasować. –

5

skończyło się tworząc pojedynczy plik adresy URL, który zawiera wszystkie szlaki chcę na 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') 

Jako efekt uboczny, to pozwoliło mi pozbyć się wszystkich pojedynczych plików urls.py w każdej aplikacji, której normalnie potrzebujesz, ale w tym przypadku cała kolekcja aplikacji wymaga ujednoliconej struktury adresów URL, więc usunięcie jest bardziej sensowne.

I następnie odwołać go z urls.py:

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

Teraz, jeśli kiedykolwiek chcesz zmienić trasy dla V2, mogę po prostu zawierać adresy v2 złożyć, jak również i ostatecznie potępiać plik V1.

+6

ale twoje aplikacje są samowystarczalne, więc powinny mieć swój własny urls.py. Tak też jest w moim przypadku i chciałbym uniknąć przenoszenia wszystkich adresów URL do scentralizowanego, co nie ma sensu. Jak ujednolicić adresy URL i jednocześnie rozdzielić aplikacje? Z pewnością istnieje sposób na włączenie istniejącego urls.py z różnych aplikacji? – dangonfast

+0

Nie znalazłem takiej drogi, myślałem, że przestałem się nią zajmować, i tak się zorientowałem. Uważam, że jest to rozsądny sposób, aby to zrobić, ponieważ * aplikacje * są rozdzielone, ale interfejs API nie jest. Dzięki temu, że REST Framework określa strukturę adresów URL dla poszczególnych aplikacji, wszystko, co robię, polega na połączeniu ich w jedno, najwyższego poziomu, który nie jest specyficzny dla żadnej aplikacji. To niewiele różni się od posiadania najwyższego poziomu adresu urls.py, który łączy pliki aplikacji urls.py poszczególnych aplikacji w ich własnym obszarze nazw. – seawolf

+1

Z całym szacunkiem nie zgadzam się: organizacja API jest odpowiedzialna za każdą aplikację, a najlepsza aplikacja nie potrzebuje wiedzy o drzewie adresów URL każdej aplikacji. Na tym polega funkcja wykrywania interfejsu API (i api_root). Najlepszy projekt będzie po prostu podłączać różne pod-API, które uzna za stosowne, w górnym wybranym punkcie końcowym. I wtedy wszystko powinno działać, łącznie z opcją przeglądania całego API zaczynając od api root. – dangonfast

6

Spowoduje to wyświetlenie wszystkich tras ViewSet pod bazowym adresem URL interfejsu API.

Określa trasy jako listę w odpowiednich dołączonych aplikacjach, dzięki czemu można je zarejestrować w innym miejscu.

Po uwzględnieniu ich w urls.py bazowej, zagnieżdżona lista list jest zbudowany i przelotowe zarejestrować wszystkie trasy na tym samym poziomie w 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]) 

Wyniki:

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

Ma to również mniej powtórzeń w każdym pliku i prawdopodobnie ułatwia czytanie.

Ponadto pozostaje całkowicie oddzielony.

Jeśli dołączona aplikacja jest używana w innym miejscu, tę samą metodę można zastosować wewnętrznie, aby zarejestrować własne trasy bez uwzględniania ich w dowolnym miejscu.

tylko spadnie na zewnątrz pętli

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

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

Doskonały pomysł. Jedyne, co można tutaj poprawić, to sądzić, że w tym przypadku mogą wystąpić kolizje nazw (jeśli na przykład dwie aplikacje próbują zarejestrować nazwę "słupków"). Ale na początek, jest to świetne podejście. Dzięki! – seawolf