Defining nested namespaces in a URLConf, for reversing Django URLs — does anyone have a cogent example?

StackOverflow https://stackoverflow.com//questions/12683494

Pergunta

I have been trying to to figure out how to define a nested URL namespace (which look:like:this) in a Django URLConf.

Before this, I figured out how to do a basic URL namespace and came up with this simple example snippet, containing what you might put in a urls.py file:

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 the deduction of which the Django documentation was, in this case, not at all helpful. While Django's doc is fantastic in all other respects, and this is an exception to the rule, there is even less information about defining nested URL namespaces.

Instead of posting my spaghettified attempts† to figure this out, I thought I might ask if anyone has, or knows of, a straightforwardly cogent and/or self-explanatory example of a URLconf that defines a nested namespace, that they could share.

Specifically I am curious about the nested parts that prefix the view: need they all be installed Django apps?

†) For the curious, here's a (probably somewhat inscrutable) example: http://imgur.com/NDn9H. I was trying to get the URLs printed out in red and green at the bottom to be named testapp:views:<viewname> instead of just testapp:<viewname>.

Foi útil?

Solução

It works rather intuitively. include a urlconf that has yet another namespaced include will result in nested namespaces.

## 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/

It's a great way to keep urls organized. I suppose the best advice I can give is to remember that include can take a patterns object directly (as in my example) which lets you use a single urls.py and split views into useful namespaces without having to create multiple urls files.

Outras dicas

UPDATE 2 (2019-10-09)

As eugene comments, UPDATE 1 no longer works for more recent versions of Django, which require an app_name to be defined in a urls.py when it's being included.

On GitHub I've created a Django project (myproject) with a couple of apps (products and books) to demonstrate how this is done to create nested namespaces. In summary, the various urls.py look like this:

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

So you can use these nested URL names like this:

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

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

UPDATE 1

Django 2.0 introduced two relevant changes. First, the urls() function is now in django.urls, so the first line of the urls.py example above would be:

from django.urls import include, url

Second, it introduce the path() function as a simpler alternative for paths that don't require a regular expression. Using that, the example urls.py would be like this:

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

ORIGINAL ANSWER

While Yuji's answer is correct, note that django.conf.urls.patterns no longer exists (since Django 1.10) and plain lists are used instead.

The same example urls.py should now be like this:

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

And still used like:

reverse('nested1:nested2:index') # should output /nested1/nested2/index/
reverse('nested1:index') # should output /nested1/index/
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top