Fixed #10061 -- Added namespacing for named URLs - most importantly, for the admin site, where the absence of this facility was causing problems. Thanks to the many people who contributed to and helped review this patch.

This change is backwards incompatible for anyone that is using the named URLs
introduced in [9739]. Any usage of the old admin_XXX names need to be modified
to use the new namespaced format; in many cases this will be as simple as a
search & replace for "admin_" -> "admin:". See the docs for more details on
the new URL names, and the namespace resolution strategy.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@11250 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Russell Keith-Magee 2009-07-16 16:16:13 +00:00
parent 9fd19c0161
commit 8d48eaa064
20 changed files with 544 additions and 119 deletions

View file

@ -1242,7 +1242,7 @@ or :attr:`AdminSite.login_template` properties.
``AdminSite`` objects
=====================
.. class:: AdminSite
.. class:: AdminSite(name=None)
A Django administrative site is represented by an instance of
``django.contrib.admin.sites.AdminSite``; by default, an instance of
@ -1256,6 +1256,14 @@ or add anything you like. Then, simply create an instance of your
Python class), and register your models and ``ModelAdmin`` subclasses
with it instead of using the default.
.. versionadded:: 1.1
When constructing an instance of an ``AdminSite``, you are able to provide
a unique instance name using the ``name`` argument to the constructor. This
instance name is used to identify the instance, especially when
:ref:`reversing admin URLs <admin-reverse-urls>`. If no instance name is
provided, a default instance name of ``admin`` will be used.
``AdminSite`` attributes
------------------------
@ -1353,10 +1361,10 @@ a pattern for your new view.
.. note::
Any view you render that uses the admin templates, or extends the base
admin template, should include in it's context a variable named
``admin_site`` that contains the name of the :class:`AdminSite` instance. For
:class:`AdminSite` instances, this means ``self.name``; for :class:`ModelAdmin`
instances, this means ``self.admin_site.name``.
admin template, should provide the ``current_app`` argument to
``RequestContext`` or ``Context`` when rendering the template. It should
be set to either ``self.name`` if your view is on an ``AdminSite`` or
``self.admin_site.name`` if your view is on a ``ModelAdmin``.
.. _admin-reverse-urls:
@ -1370,37 +1378,31 @@ accessible using Django's :ref:`URL reversing system <naming-url-patterns>`.
The :class:`AdminSite` provides the following named URL patterns:
====================== =============================== =============
Page URL name Parameters
====================== =============================== =============
Index ``admin_index``
Logout ``admin_logout``
Password change ``admin_password_change``
Password change done ``admin_password_change_done``
i18n javascript ``admin_jsi18n``
Application index page ``admin_app_list`` ``app_label``
====================== =============================== =============
These names will be prefixed with the name of the :class:`AdminSite` instance,
plus an underscore. For example, if your :class:`AdminSite` was named
``custom``, then the Logout view would be served using a URL with the name
``custom_admin_logout``. The default :class:`AdminSite` doesn't use a prefix
in it's URL names.
====================== ======================== =============
Page URL name Parameters
====================== ======================== =============
Index ``index``
Logout ``logout``
Password change ``password_change``
Password change done ``password_change_done``
i18n javascript ``jsi18n``
Application index page ``app_list`` ``app_label``
====================== ======================== =============
Each :class:`ModelAdmin` instance provides an additional set of named URLs:
====================== ===================================================== =============
Page URL name Parameters
====================== ===================================================== =============
Changelist ``admin_{{ app_label }}_{{ model_name }}_changelist``
Add ``admin_{{ app_label }}_{{ model_name }}_add``
History ``admin_{{ app_label }}_{{ model_name }}_history`` ``object_id``
Delete ``admin_{{ app_label }}_{{ model_name }}_delete`` ``object_id``
Change ``admin_{{ app_label }}_{{ model_name }}_change`` ``object_id``
====================== ===================================================== =============
====================== =============================================== =============
Page URL name Parameters
====================== =============================================== =============
Changelist ``{{ app_label }}_{{ model_name }}_changelist``
Add ``{{ app_label }}_{{ model_name }}_add``
History ``{{ app_label }}_{{ model_name }}_history`` ``object_id``
Delete ``{{ app_label }}_{{ model_name }}_delete`` ``object_id``
Change ``{{ app_label }}_{{ model_name }}_change`` ``object_id``
====================== =============================================== =============
Again, these names will be prefixed by the name of the :class:`AdminSite` in
which they are deployed.
These named URLs are registered with the application namespace ``admin``, and
with an instance namespace corresponding to the name of the Site instance.
So - if you wanted to get a reference to the Change view for a particular
``Choice`` object (from the polls application) in the default admin, you would
@ -1408,8 +1410,16 @@ call::
>>> from django.core import urlresolvers
>>> c = Choice.objects.get(...)
>>> change_url = urlresolvers.reverse('admin_polls_choice_change', args=(c.id,))
>>> change_url = urlresolvers.reverse('admin:polls_choice_change', args=(c.id,))
However, if the admin instance was named ``custom``, you would need to call::
This will find the first registered instance of the admin application (whatever the instance
name), and resolve to the view for changing ``poll.Choice`` instances in that instance.
>>> change_url = urlresolvers.reverse('custom_admin_polls_choice_change', args=(c.id,))
If you want to find a URL in a specific admin instance, provide the name of that instance
as a ``current_app`` hint to the reverse call. For example, if you specifically wanted
the admin view from the admin instance named ``custom``, you would need to call::
>>> change_url = urlresolvers.reverse('custom:polls_choice_change', args=(c.id,))
For more details, see the documentation on :ref:`reversing namespaced URLs
<topics-http-reversing-url-namespaces>`.

View file

@ -86,9 +86,16 @@ Rendering a context
Once you have a compiled ``Template`` object, you can render a context -- or
multiple contexts -- with it. The ``Context`` class lives at
``django.template.Context``, and the constructor takes one (optional)
argument: a dictionary mapping variable names to variable values. Call the
``Template`` object's ``render()`` method with the context to "fill" the
``django.template.Context``, and the constructor takes two (optional)
arguments:
* A dictionary mapping variable names to variable values.
* The name of the current application. This application name is used
to help :ref:`resolve namespaced URLs<topics-http-reversing-url-namespaces>`.
If you're not using namespaced URLs, you can ignore this argument.
Call the ``Template`` object's ``render()`` method with the context to "fill" the
template::
>>> from django.template import Context, Template
@ -549,13 +556,13 @@ Here are the template loaders that come with Django:
Note that the loader performs an optimization when it is first imported: It
caches a list of which :setting:`INSTALLED_APPS` packages have a
``templates`` subdirectory.
This loader is enabled by default.
``django.template.loaders.eggs.load_template_source``
Just like ``app_directories`` above, but it loads templates from Python
eggs rather than from the filesystem.
This loader is disabled by default.
Django uses the template loaders in order according to the

View file

@ -795,6 +795,16 @@ missing. In practice you'll use this to link to views that are optional::
<a href="{{ the_url }}">Link to optional stuff</a>
{% endif %}
.. versionadded:: 1.1
If you'd like to retrieve a namespaced URL, specify the fully qualified name::
{% url myapp:view-name %}
This will follow the normal :ref:`namespaced URL resolution strategy
<topics-http-reversing-url-namespaces>`, including using any hints provided
by the context as to the current application.
.. templatetag:: widthratio
widthratio

View file

@ -400,7 +400,7 @@ further processing.
.. versionadded:: 1.1
Another posibility is to include additional URL patterns not by specifying the
Another possibility is to include additional URL patterns not by specifying the
URLconf Python module defining them as the `include`_ argument but by using
directly the pattern list as returned by `patterns`_ instead. For example::
@ -417,6 +417,13 @@ directly the pattern list as returned by `patterns`_ instead. For example::
(r'^credit/', include(extra_patterns)),
)
This approach can be seen in use when you deploy an instance of the Django
Admin application. The Django Admin is deployed as instances of a
:class:`AdminSite`; each :class:`AdminSite` instance has an attribute
``urls`` that returns the url patterns available to that instance. It is this
attribute that you ``included()`` into your projects ``urlpatterns`` when you
deploy the admin instance.
.. _`Django Web site`: http://www.djangoproject.com/
Captured parameters
@ -439,6 +446,58 @@ the following example is valid::
In the above example, the captured ``"username"`` variable is passed to the
included URLconf, as expected.
.. _topics-http-defining-url-namespaces:
Defining URL Namespaces
-----------------------
When you need to deploying multiple instances of a single application, it can
be helpful to be able to differentiate between instances. This is especially
important when using _`named URL patterns <naming-url-patterns>`, since
multiple instances of a single application will share named URLs. Namespaces
provide a way to tell these named URLs apart.
A URL namespace comes in two parts, both of which are strings:
* An **application namespace**. This describes the name of the application
that is being deployed. Every instance of a single application will have
the same application namespace. For example, Django's admin application
has the somewhat predictable application namespace of ``admin``.
* An **instance namespace**. This identifies a specific instance of an
application. Instance namespaces should be unique across your entire
project. However, and instance namespace can be the same as the
application namespace. This is used to specify a default instance of an
application. For example, the default Django Admin instance has an
instance namespace of ``admin``.
URL Namespaces can be specified in two ways.
Firstly, you can provide the applicaiton and instance namespace as arguments
to the ``include()`` when you construct your URL patterns. For example,::
(r'^help/', include('apps.help.urls', namespace='foo', app_name='bar')),
This will include the URLs defined in ``apps.help.urls`` into the application
namespace ``bar``, with the instance namespace ``foo``.
Secondly, you can include an object that contains embedded namespace data. If
you ``include()`` a ``patterns`` object, that object will be added to the
global namespace. However, you can also ``include()`` an object that contains
a 3-tuple containing::
(<patterns object>, <application namespace>, <instance namespace>)
This will include the nominated URL patterns into the given application and
instance namespace. For example, the ``urls`` attribute of Django's
:class:`AdminSite` object returns a 3-tuple that contains all the patterns in
an admin site, plus the name of the admin instance, and the application
namespace ``admin``.
Once you have defined namespace URLs, you can reverse them. For details on
reversing namespaced urls, see the documentation on :ref:`reversing namespaced
URLs <topics-http-reversing-url-namespaces>`.
Passing extra options to view functions
=======================================
@ -613,6 +672,86 @@ not restricted to valid Python names.
name, will decrease the chances of collision. We recommend something like
``myapp-comment`` instead of ``comment``.
.. _topics-http-reversing-url-namespaces:
URL namespaces
--------------
.. versionadded:: 1.1
Namespaced URLs are specified using the `:` operator. For example, the main index
page of the admin application is referenced using ``admin:index``. This indicates
a namespace of ``admin``, and a named URL of ``index``.
Namespaces can also be nested. The named URL ``foo:bar:whiz`` would look for
a pattern named ``whiz`` in the namespace ``bar`` that is itself defined within
the top-level namespace ``foo``.
When given a namespaced URL (e.g.,, `myapp:index`) to resolve, Django splits
the fully qualified name into parts, and then tries the following lookup:
1. Django then looks for a matching application namespace (in this
example, ``myapp``). This will yield a list of instances of that
application.
2. If there is a ``current`` application defined, Django finds and returns
the URL resolver for that instance. The ``current`` can be specified
as an attribute on the template context - applications that expect to
have multiple deployments should set the ``current_app`` attribute on
any ``Context`` or ``RequestContext`` that is used to render a
template.
The current application can also be specified manually as an argument
to the :method:``reverse()`` function.
3. If there is no current application. Django looks for a default
application instance. The default application instance is the instance
that has an instance namespace matching the application namespace (in
this example, an instance of the ``myapp`` called ``myapp``)
4. If there is no default application instance, Django will pick the first
deployed instance of the application, whatever it's instance name may be.
5. If the provided namespace doesn't match an application namespace in
step 2, Django will attempt a direct lookup of the namespace as an
instance namespace.
If there are nested namespaces, these steps are repeated for each part of the
namespace until only the view name is unresolved. The view name will then be
resolved into a URL in the namespace that has been found.
To show this resolution strategy in action, consider an example of two instances
of ``myapp``: one called ``foo``, and one called ``bar``. ``myapp`` has a main
index page with a URL named `index`. Using this setup, the following lookups are
possible:
* If one of the instances is current - say, if we were rendering a utility page
in the instance ``bar`` - ``myapp:index`` will resolve to the index page of
the instance ``bar``.
* If there is no current instance - say, if we were rendering a page
somewhere else on the site - ``myapp:index`` will resolve to the first
registered instance of ``myapp``. Since there is no default instance,
the first instance of ``myapp`` that is registered will be used. This could
be ``foo`` or ``bar``, depending on the order they are introduced into the
urlpatterns of the project.
* ``foo:index`` will always resolve to the index page of the instance ``foo``.
If there was also a default instance - i.e., an instance named `myapp` - the
following would happen:
* If one of the instances is current - say, if we were rendering a utility page
in the instance ``bar`` - ``myapp:index`` will resolve to the index page of
the instance ``bar``.
* If there is no current instance - say, if we were rendering a page somewhere
else on the site - ``myapp:index`` will resolve to the index page of the
default instance.
* ``foo:index`` will again resolve to the index page of the instance ``foo``.
Utility methods
===============
@ -624,7 +763,7 @@ your code, Django provides the following method (in the
``django.core.urlresolvers`` module):
.. currentmodule:: django.core.urlresolvers
.. function:: reverse(viewname, urlconf=None, args=None, kwargs=None)
.. function:: reverse(viewname, urlconf=None, args=None, kwargs=None, current_app=None)
``viewname`` is either the function name (either a function reference, or the
string version of the name, if you used that form in ``urlpatterns``) or the
@ -646,6 +785,14 @@ vertical bar (``"|"``) character. You can quite happily use such patterns for
matching against incoming URLs and sending them off to views, but you cannot
reverse such patterns.
.. versionadded:: 1.1
The ``current_app`` argument allows you to provide a hint to the resolver
indicating the application to which the currently executing view belongs.
This ``current_app`` argument is used as a hint to resolve application
namespaces into URLs on specific application instances, according to the
:ref:`namespaced URL resolution strategy <topics-http-reversing-url-namespaces>`.
.. admonition:: Make sure your views are all correct
As part of working out which URL names map to which patterns, the