Commit 8d48eaa0 authored by Russell Keith-Magee's avatar Russell Keith-Magee
Browse files

Fixed #10061 -- Added namespacing for named URLs - most importantly, for the...

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
parent 9fd19c01
Loading
Loading
Loading
Loading
+13 −3
Original line number Diff line number Diff line
@@ -6,7 +6,16 @@ __all__ = ['handler404', 'handler500', 'include', 'patterns', 'url']
handler404 = 'django.views.defaults.page_not_found'
handler500 = 'django.views.defaults.server_error'

include = lambda urlconf_module: [urlconf_module]
def include(arg, namespace=None, app_name=None):
    if isinstance(arg, tuple):
        # callable returning a namespace hint
        if namespace:
            raise ImproperlyConfigured('Cannot override the namespace for a dynamic module that provides a namespace')
        urlconf_module, app_name, namespace = arg
    else:
        # No namespace hint - use manually provided namespace
        urlconf_module = arg
    return (urlconf_module, app_name, namespace)

def patterns(prefix, *args):
    pattern_list = []
@@ -19,9 +28,10 @@ def patterns(prefix, *args):
    return pattern_list

def url(regex, view, kwargs=None, name=None, prefix=''):
    if type(view) == list:
    if isinstance(view, (list,tuple)):
        # For include(...) processing.
        return RegexURLResolver(regex, view[0], kwargs)
        urlconf_module, app_name, namespace = view
        return RegexURLResolver(regex, urlconf_module, kwargs, app_name=app_name, namespace=namespace)
    else:
        if isinstance(view, basestring):
            if not view:
+14 −10
Original line number Diff line number Diff line
@@ -226,24 +226,24 @@ class ModelAdmin(BaseModelAdmin):
                return self.admin_site.admin_view(view)(*args, **kwargs)
            return update_wrapper(wrapper, view)

        info = self.admin_site.name, self.model._meta.app_label, self.model._meta.module_name
        info = self.model._meta.app_label, self.model._meta.module_name

        urlpatterns = patterns('',
            url(r'^$',
                wrap(self.changelist_view),
                name='%sadmin_%s_%s_changelist' % info),
                name='%s_%s_changelist' % info),
            url(r'^add/$',
                wrap(self.add_view),
                name='%sadmin_%s_%s_add' % info),
                name='%s_%s_add' % info),
            url(r'^(.+)/history/$',
                wrap(self.history_view),
                name='%sadmin_%s_%s_history' % info),
                name='%s_%s_history' % info),
            url(r'^(.+)/delete/$',
                wrap(self.delete_view),
                name='%sadmin_%s_%s_delete' % info),
                name='%s_%s_delete' % info),
            url(r'^(.+)/$',
                wrap(self.change_view),
                name='%sadmin_%s_%s_change' % info),
                name='%s_%s_change' % info),
        )
        return urlpatterns

@@ -582,11 +582,12 @@ class ModelAdmin(BaseModelAdmin):
            'save_on_top': self.save_on_top,
            'root_path': self.admin_site.root_path,
        })
        context_instance = template.RequestContext(request, current_app=self.admin_site.name)
        return render_to_response(self.change_form_template or [
            "admin/%s/%s/change_form.html" % (app_label, opts.object_name.lower()),
            "admin/%s/change_form.html" % app_label,
            "admin/change_form.html"
        ], context, context_instance=template.RequestContext(request))
        ], context, context_instance=context_instance)

    def response_add(self, request, obj, post_url_continue='../%s/'):
        """
@@ -977,11 +978,12 @@ class ModelAdmin(BaseModelAdmin):
            'actions_on_bottom': self.actions_on_bottom,
        }
        context.update(extra_context or {})
        context_instance = template.RequestContext(request, current_app=self.admin_site.name)
        return render_to_response(self.change_list_template or [
            'admin/%s/%s/change_list.html' % (app_label, opts.object_name.lower()),
            'admin/%s/change_list.html' % app_label,
            'admin/change_list.html'
        ], context, context_instance=template.RequestContext(request))
        ], context, context_instance=context_instance)

    def delete_view(self, request, object_id, extra_context=None):
        "The 'delete' admin view for this model."
@@ -1032,11 +1034,12 @@ class ModelAdmin(BaseModelAdmin):
            "app_label": app_label,
        }
        context.update(extra_context or {})
        context_instance = template.RequestContext(request, current_app=self.admin_site.name)
        return render_to_response(self.delete_confirmation_template or [
            "admin/%s/%s/delete_confirmation.html" % (app_label, opts.object_name.lower()),
            "admin/%s/delete_confirmation.html" % app_label,
            "admin/delete_confirmation.html"
        ], context, context_instance=template.RequestContext(request))
        ], context, context_instance=context_instance)

    def history_view(self, request, object_id, extra_context=None):
        "The 'history' admin view for this model."
@@ -1059,11 +1062,12 @@ class ModelAdmin(BaseModelAdmin):
            'app_label': app_label,
        }
        context.update(extra_context or {})
        context_instance = template.RequestContext(request, current_app=self.admin_site.name)
        return render_to_response(self.object_history_template or [
            "admin/%s/%s/object_history.html" % (app_label, opts.object_name.lower()),
            "admin/%s/object_history.html" % app_label,
            "admin/object_history.html"
        ], context, context_instance=template.RequestContext(request))
        ], context, context_instance=context_instance)

    #
    # DEPRECATED methods.
+24 −20
Original line number Diff line number Diff line
@@ -5,6 +5,7 @@ from django.contrib.admin import actions
from django.contrib.auth import authenticate, login
from django.db.models.base import ModelBase
from django.core.exceptions import ImproperlyConfigured
from django.core.urlresolvers import reverse
from django.shortcuts import render_to_response
from django.utils.functional import update_wrapper
from django.utils.safestring import mark_safe
@@ -38,17 +39,14 @@ class AdminSite(object):
    login_template = None
    app_index_template = None

    def __init__(self, name=None):
    def __init__(self, name=None, app_name='admin'):
        self._registry = {} # model_class class -> admin_class instance
        # TODO Root path is used to calculate urls under the old root() method
        # in order to maintain backwards compatibility we are leaving that in
        # so root_path isn't needed, not sure what to do about this.
        self.root_path = 'admin/'
        self.root_path = None
        if name is None:
            name = ''
            self.name = 'admin'
        else:
            name += '_'
            self.name = name
        self.app_name = app_name
        self._actions = {'delete_selected': actions.delete_selected}
        self._global_actions = self._actions.copy()

@@ -202,24 +200,24 @@ class AdminSite(object):
        urlpatterns = patterns('',
            url(r'^$',
                wrap(self.index),
                name='%sadmin_index' % self.name),
                name='index'),
            url(r'^logout/$',
                wrap(self.logout),
                name='%sadmin_logout'),
                name='logout'),
            url(r'^password_change/$',
                wrap(self.password_change, cacheable=True),
                name='%sadmin_password_change' % self.name),
                name='password_change'),
            url(r'^password_change/done/$',
                wrap(self.password_change_done, cacheable=True),
                name='%sadmin_password_change_done' % self.name),
                name='password_change_done'),
            url(r'^jsi18n/$',
                wrap(self.i18n_javascript, cacheable=True),
                name='%sadmin_jsi18n' % self.name),
                name='jsi18n'),
            url(r'^r/(?P<content_type_id>\d+)/(?P<object_id>.+)/$',
                'django.views.defaults.shortcut'),
            url(r'^(?P<app_label>\w+)/$',
                wrap(self.app_index),
                name='%sadmin_app_list' % self.name),
                name='app_list')
        )

        # Add in each model's views.
@@ -231,7 +229,7 @@ class AdminSite(object):
        return urlpatterns

    def urls(self):
        return self.get_urls()
        return self.get_urls(), self.app_name, self.name
    urls = property(urls)

    def password_change(self, request):
@@ -239,8 +237,11 @@ class AdminSite(object):
        Handles the "change password" task -- both form display and validation.
        """
        from django.contrib.auth.views import password_change
        return password_change(request,
            post_change_redirect='%spassword_change/done/' % self.root_path)
        if self.root_path is not None:
            url = '%spassword_change/done/' % self.root_path
        else:
            url = reverse('admin:password_change_done', current_app=self.name)
        return password_change(request, post_change_redirect=url)

    def password_change_done(self, request):
        """
@@ -368,8 +369,9 @@ class AdminSite(object):
            'root_path': self.root_path,
        }
        context.update(extra_context or {})
        context_instance = template.RequestContext(request, current_app=self.name)
        return render_to_response(self.index_template or 'admin/index.html', context,
            context_instance=template.RequestContext(request)
            context_instance=context_instance
        )
    index = never_cache(index)

@@ -382,8 +384,9 @@ class AdminSite(object):
            'root_path': self.root_path,
        }
        context.update(extra_context or {})
        context_instance = template.RequestContext(request, current_app=self.name)
        return render_to_response(self.login_template or 'admin/login.html', context,
            context_instance=template.RequestContext(request)
            context_instance=context_instance
        )

    def app_index(self, request, app_label, extra_context=None):
@@ -425,9 +428,10 @@ class AdminSite(object):
            'root_path': self.root_path,
        }
        context.update(extra_context or {})
        context_instance = template.RequestContext(request, current_app=self.name)
        return render_to_response(self.app_index_template or ('admin/%s/app_index.html' % app_label,
            'admin/app_index.html'), context,
            context_instance=template.RequestContext(request)
            context_instance=context_instance
        )

    def root(self, request, url):
+24 −1
Original line number Diff line number Diff line
@@ -23,7 +23,30 @@
        {% block branding %}{% endblock %}
        </div>
        {% if user.is_authenticated and user.is_staff %}
        <div id="user-tools">{% trans 'Welcome,' %} <strong>{% firstof user.first_name user.username %}</strong>. {% block userlinks %}{% url django-admindocs-docroot as docsroot %}{% if docsroot %}<a href="{{ docsroot }}">{% trans 'Documentation' %}</a> / {% endif %}<a href="{{ root_path }}password_change/">{% trans 'Change password' %}</a> / <a href="{{ root_path }}logout/">{% trans 'Log out' %}</a>{% endblock %}</div>
        <div id="user-tools">
            {% trans 'Welcome,' %}
            <strong>{% firstof user.first_name user.username %}</strong>.
            {% block userlinks %}
                {% url django-admindocs-docroot as docsroot %}
                {% if docsroot %}
                    <a href="{{ docsroot }}">{% trans 'Documentation' %}</a> /
                {% endif %}
                {% url admin:password_change as password_change_url %}
                {% if password_change_url %}
                    <a href="{{ password_change_url }}">
                {% else %}
                    <a href="{{ root_path }}password_change/">
                {% endif %}
                {% trans 'Change password' %}</a> /
                {% url admin:logout as logout_url %}
                {% if logout_url %}
                    <a href="{{ logout_url }}">
                {% else %}
                    <a href="{{ root_path }}logout/">
                {% endif %}
                {% trans 'Log out' %}</a>
            {% endblock %}
        </div>
        {% endif %}
        {% block nav-global %}{% endblock %}
    </div>
+7 −8
Original line number Diff line number Diff line
@@ -222,8 +222,7 @@ class RelatedFieldWidgetWrapper(forms.Widget):
        rel_to = self.rel.to
        info = (rel_to._meta.app_label, rel_to._meta.object_name.lower())
        try:
            related_info = (self.admin_site.name,) + info
            related_url = reverse('%sadmin_%s_%s_add' % related_info)
            related_url = reverse('admin:%s_%s_add' % info, current_app=self.admin_site.name)
        except NoReverseMatch:
            related_url = '../../../%s/%s/add/' % info
        self.widget.choices = self.choices
Loading