Определение вложенных пространств имен в URLConf, для обращения к URL-адресам Django - есть ли у кого-нибудь убедительный пример?

Я пытался выяснить, как определить вложенное пространство имен URL (которое look:like:this) в Django URLConf.

Перед этим я выяснил, как создать простое пространство имен URL, и придумал простой пример фрагмента, содержащий то, что вы можете поместить в файл urls.py:

from django.conf.urls import patterns, include, url

# you can only define a namespace for urls when calling include():

app_patterns = patterns('',
    url(r'^(?P<pk>[\w\-]+)/$', 'yourapp.views.your_view_function',
        name="your-view"),
)

urlpatterns = patterns('',
    url(r'^view-function/', include(app_patterns,
        namespace='yournamespace', app_name='yourapp')),
)

"""

    You can now use the namespace when you refer to the view, e.g. a call
    to 'reverse()':

    # yourapp/models.py

    from django.core.urlresolvers import reverse

    # ...

    class MyModel(models.Model):

        def get_absolute_url(self):
            return reverse('signalqueue:exception-log-entry', kwargs=dict(pk=self.pk))

"""

... w/r/t, вывод, из которого документация Django, в данном случае, совсем не полезна. Хотя Django doc является фантастическим во всех других отношениях, и это исключение из правил, информации об определении вложенных пространств имен URL еще меньше.

Вместо того, чтобы публиковать мои спагетизированные попытки †, чтобы выяснить это, я подумал, что могу спросить, есть ли у кого-либо прямой или убедительный пример убедительного и/или самоочевидного примера URLconf, который определяет вложенное пространство имен, которым они могут поделиться.

В частности, мне любопытно, какие вложенные части ставят префикс представления: нужно ли устанавливать все приложения Django?

†) Для любопытных приведем (возможно, несколько непонятный) пример: http://imgur.com/NDn9H.Я пытался распечатать URL-адреса красным и зеленым цветом внизу, чтобы они назывались testapp:views:<viewname> вместо просто testapp:<viewname>.

Ответ 1

Это работает довольно интуитивно. include urlconf, у которого есть еще одно пространство имен include, приведет к вложенным пространствам имен.

## urls.py
nested2 = patterns('',
   url(r'^index/$', 'index', name='index'),
)

nested1 = patterns('',
   url(r'^nested2/', include(nested2, namespace="nested2"),
   url(r'^index/$', 'index', name='index'),
)   

urlpatterns = patterns('',
   (r'^nested1/', include(nested1, namespace="nested1"),
)

reverse('nested1:nested2:index') # should output /nested1/nested2/index/
reverse('nested1:index') # should output /nested1/index/

Это отличный способ сохранить URL-адреса. Я полагаю, что лучший совет, который я могу дать, - помнить, что include может принимать объект patterns напрямую (как в моем примере), который позволяет использовать один urls.py и разделять представления на полезные пространства имен, не создавая несколько URL-адресов файлы.

Ответ 2

ОБНОВЛЕНИЕ 2 (2019-10-09)

Как заметил Евгений, UPDATE 1 больше не работает для более свежих версий Django, для которых требуется, чтобы app_name был определен в urls.py, когда он был включен.

На GitHub я создал проект Django (myproject) с парой приложений (products и books), чтобы продемонстрировать, как это делается для создания вложенных пространств имен. Таким образом, различные urls.py выглядят так:

# myproject/urls.py
from django.urls import include, path
from products import urls as products_urls
from products import views

urlpatterns = [
    path("", views.site_home, name="home"),
    path("products/", include(products_urls, namespace="products"),)
]
# products/urls.py
from django.urls import include, path
from books import urls as books_urls
from . import views

app_name = "products"

urlpatterns = [
    path("", views.index, name="product_index"),
    path("books/", include(books_urls, namespace="books")),
]
# books/urls.py
from django.urls import path
from . import views

app_name = "books"

urlpatterns = [
    path("", views.index, name="book_index"),
    path("<slug:book_slug>/", views.detail, name="book_detail"),
]

Таким образом, вы можете использовать эти вложенные имена URL, например так:

reverse("products:books:book_index")
# '/products/books/'

reverse("products:books:book_detail", kwargs={"book_slug": "my-book"})
# '/products/books/my-book/'

ОБНОВЛЕНИЕ 1

В Django 2.0 внесены два важных изменения. Во-первых, функция urls() теперь находится в django.urls, поэтому первая строка приведенного выше примера urls.py будет выглядеть так:

from django.urls import include, url

Во-вторых, он представляет функцию path() как более простую альтернативу для путей, которые не требуют регулярного выражения. Используя это, пример urls.py будет выглядеть следующим образом:

from django.urls import include, path

nested2 = [
   path('index/', 'index', name='index'),
]   

nested1 = [
   path('nested2/', include(nested2, namespace='nested2')),
   path('index/', 'index', name='index'),
]   

urlpatterns = [
   path('nested1/', include(nested1, namespace='nested1')),
]

ОРИГИНАЛЬНЫЙ ОТВЕТ

Хотя ответ юджи правильный, обратите внимание, что django.conf.urls.patterns больше не существует (начиная с Django 1.10), и вместо него используются простые списки.

Тот же пример urls.py теперь должен выглядеть следующим образом:

from django.conf.urls import include, url

nested2 = [
   url(r'^index/$', 'index', name='index'),
]   

nested1 = [
   url(r'^nested2/', include(nested2, namespace='nested2')),
   url(r'^index/$', 'index', name='index'),
]   

urlpatterns = [
   url(r'^nested1/', include(nested1, namespace='nested1')),
]   

И до сих пор используется как:

reverse('nested1:nested2:index') # should output /nested1/nested2/index/
reverse('nested1:index') # should output /nested1/index/