Ticket #6470: admin-urlpatterns-6470.2.diff

File admin-urlpatterns-6470.2.diff, 15.5 KB (added by Alex Gaynor, 16 years ago)

Latest version, full tests pass, and should be backwards compatible, updated docs

  • django/conf/project_template/urls.py

    diff --git a/django/conf/project_template/urls.py b/django/conf/project_template/urls.py
    index af1d1db..dfb49d3 100644
    a b urlpatterns = patterns('',  
    1313    # (r'^admin/doc/', include('django.contrib.admindocs.urls')),
    1414
    1515    # Uncomment the next line to enable the admin:
    16     # (r'^admin/(.*)', admin.site.root),
     16    # (r'^admin/', include(admin.site.urls)),
    1717)
  • django/contrib/admin/options.py

    diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py
    index 019bfe9..39ab744 100644
    a b  
     1import types
     2
    13from django import forms, template
    24from django.forms.formsets import all_valid
    35from django.forms.models import modelform_factory, inlineformset_factory
    from django.forms.models import BaseInlineFormSet  
    57from django.contrib.contenttypes.models import ContentType
    68from django.contrib.admin import widgets
    79from django.contrib.admin import helpers
    8 from django.contrib.admin.util import quote, unquote, flatten_fieldsets, get_deleted_objects
     10from django.contrib.admin.util import quote, unquote, flatten_fieldsets, get_deleted_objects, admin_perm_test
    911from django.core.exceptions import PermissionDenied
    1012from django.db import models, transaction
    1113from django.http import Http404, HttpResponse, HttpResponseRedirect
    class ModelAdmin(BaseModelAdmin):  
    196198        else:
    197199            return self.change_view(request, unquote(url))
    198200
     201    def _get_urls(self):
     202        from django.conf.urls.defaults import patterns, url
     203        urls_module = types.ModuleType('%s.urls' % self.__class__.__name__)
     204        info = self.model._meta.app_label, self.model._meta.module_name
     205        urlpatterns = patterns('',
     206            url(r'^$', lambda *args, **kwargs: self.changelist_view(*args, **kwargs), name='admin_%s_%s_changelist' % info),
     207            url(r'^add/$', lambda *args, **kwargs: self.add_view(*args, **kwargs), name='admin_%s_%s_add' % info),
     208            url(r'^(.+)/history/$', lambda *args, **kwargs: self.history_view(*args, **kwargs), name='admin_%s_%s_history' % info),
     209            url(r'^(.+)/delete/$', lambda *args, **kwargs: self.delete_view(*args, **kwargs), name='admin_%s_%s_delete' % info),
     210            url(r'^(.+)/$', lambda *args, **kwargs: self.change_view(*args, **kwargs), name='admin_%s_%s_change' % info),
     211        )
     212        urls_module.urlpatterns = urlpatterns
     213        return urls_module
     214    urls = property(_get_urls)
     215 
     216
    199217    def _media(self):
    200218        from django.conf import settings
    201219
    class ModelAdmin(BaseModelAdmin):  
    537555        }
    538556        context.update(extra_context or {})
    539557        return self.render_change_form(request, context, add=True)
    540     add_view = transaction.commit_on_success(add_view)
     558    add_view = transaction.commit_on_success(admin_perm_test(add_view))
    541559
    542560    def change_view(self, request, object_id, extra_context=None):
    543561        "The 'change' admin view for this model."
    class ModelAdmin(BaseModelAdmin):  
    545563        opts = model._meta
    546564
    547565        try:
    548             obj = model._default_manager.get(pk=object_id)
     566            obj = model._default_manager.get(pk=unquote(object_id))
    549567        except model.DoesNotExist:
    550568            # Don't raise Http404 just yet, because we haven't checked
    551569            # permissions yet. We don't want an unauthenticated user to be able
    class ModelAdmin(BaseModelAdmin):  
    615633        }
    616634        context.update(extra_context or {})
    617635        return self.render_change_form(request, context, change=True, obj=obj)
    618     change_view = transaction.commit_on_success(change_view)
     636    change_view = transaction.commit_on_success(admin_perm_test(change_view))
    619637
    620638    def changelist_view(self, request, extra_context=None):
    621639        "The 'change list' admin view for this model."
    class ModelAdmin(BaseModelAdmin):  
    651669            'admin/%s/change_list.html' % app_label,
    652670            'admin/change_list.html'
    653671        ], context, context_instance=template.RequestContext(request))
     672    changelist_view = admin_perm_test(changelist_view)
    654673
    655674    def delete_view(self, request, object_id, extra_context=None):
    656675        "The 'delete' admin view for this model."
    class ModelAdmin(BaseModelAdmin):  
    658677        app_label = opts.app_label
    659678
    660679        try:
    661             obj = self.model._default_manager.get(pk=object_id)
     680            obj = self.model._default_manager.get(pk=unquote(object_id))
    662681        except self.model.DoesNotExist:
    663682            # Don't raise Http404 just yet, because we haven't checked
    664683            # permissions yet. We don't want an unauthenticated user to be able
    class ModelAdmin(BaseModelAdmin):  
    673692
    674693        # Populate deleted_objects, a data structure of all related objects that
    675694        # will also be deleted.
    676         deleted_objects = [mark_safe(u'%s: <a href="../../%s/">%s</a>' % (escape(force_unicode(capfirst(opts.verbose_name))), quote(object_id), escape(obj))), []]
     695        deleted_objects = [mark_safe(u'%s: <a href="../../%s/">%s</a>' % (escape(force_unicode(capfirst(opts.verbose_name))), object_id, escape(obj))), []]
    677696        perms_needed = set()
    678697        get_deleted_objects(deleted_objects, perms_needed, request.user, obj, opts, 1, self.admin_site)
    679698
    class ModelAdmin(BaseModelAdmin):  
    706725            "admin/%s/delete_confirmation.html" % app_label,
    707726            "admin/delete_confirmation.html"
    708727        ], context, context_instance=template.RequestContext(request))
     728    delete_view = admin_perm_test(delete_view)
    709729
    710730    def history_view(self, request, object_id, extra_context=None):
    711731        "The 'history' admin view for this model."
    class ModelAdmin(BaseModelAdmin):  
    733753            "admin/%s/object_history.html" % app_label,
    734754            "admin/object_history.html"
    735755        ], context, context_instance=template.RequestContext(request))
     756    history_view = admin_perm_test(history_view)
    736757
    737758class InlineModelAdmin(BaseModelAdmin):
    738759    """
  • django/contrib/admin/sites.py

    diff --git a/django/contrib/admin/sites.py b/django/contrib/admin/sites.py
    index c16ab6a..a2a9aba 100644
    a b  
    11import base64
    22import re
     3import types
     4
    35from django import http, template
    46from django.contrib.admin import ModelAdmin
     7from django.contrib.admin.util import admin_perm_test
    58from django.contrib.auth import authenticate, login
    69from django.db.models.base import ModelBase
    710from django.core.exceptions import ImproperlyConfigured
    class AdminSite(object):  
    3639
    3740    def __init__(self):
    3841        self._registry = {} # model_class class -> admin_class instance
     42        # TODO Root path is used to calculate urls under the old root() method
     43        # in order to maintain backwards compatibility we are leaving that in
     44        # so root_path isn't needed, not sure what to do about this.
     45        self.root_path = 'admin/'
    3946
    4047    def register(self, model_or_iterable, admin_class=None, **options):
    4148        """
    class AdminSite(object):  
    121128
    122129        `url` is the remainder of the URL -- e.g. 'comments/comment/'.
    123130        """
     131        import warnings
     132        warnings.warn("Using AdminSite.root() is deprecated, you should \
     133            include(AdminSite.urls) instead", PendingDeprecationWarning)
    124134        if request.method == 'GET' and not request.path.endswith('/'):
    125135            return http.HttpResponseRedirect(request.path + '/')
    126136
    class AdminSite(object):  
    159169                return self.app_index(request, url)
    160170
    161171        raise http.Http404('The requested admin page does not exist.')
    162 
     172   
     173    def _get_urls(self):
     174        from django.conf.urls.defaults import patterns, url, include
     175        from django.core.urlresolvers import RegexURLResolver
     176        urls_module = types.ModuleType('%s.urls' % self.__class__.__name__)
     177        urlpatterns = patterns('',
     178            url(r'^$', lambda *args, **kwargs: self.index(*args, **kwargs), name='admin_index'),
     179            url(r'^logout/$', lambda *args, **kwargs: self.logout(*args, **kwargs), name='admin_logout'),
     180            url(r'^password_change/$', lambda *args, **kwargs: self.password_change(*args, **kwargs), name='admin_password_change'),
     181            url(r'^password_change/done/$', lambda *args, **kwargs: self.password_change_done(*args, **kwargs), name='admin_password_change_done'),
     182            url(r'^jsi18n/$', lambda *args, **kwargs: self.i18n_javascript(*args, **kwargs), name='admin_jsi18n'),
     183            url('^r/(?P<content_type_id>\d+)/(?P<object_id>.+)/$', 'django.views.defaults.shortcut'),
     184            url('^(?P<app_label>\w+)/$', lambda *args, **kwargs: self.app_index(*args, **kwargs)),
     185        )
     186        for model, model_admin in self._registry.iteritems():
     187            urlpatterns += patterns('',
     188                url('^%s/%s/' % (model._meta.app_label, model._meta.module_name), include(model_admin.urls))
     189            )
     190        urls_module.urlpatterns = urlpatterns
     191        return urls_module
     192    urls = property(_get_urls)
     193   
    163194    def model_page(self, request, app_label, model_name, rest_of_url=None):
    164195        """
    165196        Handles the model-specific functionality of the admin site, delegating
    class AdminSite(object):  
    183214        from django.contrib.auth.views import password_change
    184215        return password_change(request,
    185216            post_change_redirect='%spassword_change/done/' % self.root_path)
     217    passoword_change = admin_perm_test(password_change)
    186218
    187219    def password_change_done(self, request):
    188220        """
    class AdminSite(object):  
    190222        """
    191223        from django.contrib.auth.views import password_change_done
    192224        return password_change_done(request)
     225    password_change_done = admin_perm_test(password_change_done)
    193226
    194227    def i18n_javascript(self, request):
    195228        """
    class AdminSite(object):  
    203236        else:
    204237            from django.views.i18n import null_javascript_catalog as javascript_catalog
    205238        return javascript_catalog(request, packages='django.conf')
     239    i18n_javascript = admin_perm_test(i18n_javascript)
    206240
    207241    def logout(self, request):
    208242        """
    class AdminSite(object):  
    317351        return render_to_response(self.index_template or 'admin/index.html', context,
    318352            context_instance=template.RequestContext(request)
    319353        )
    320     index = never_cache(index)
     354    index = never_cache(admin_perm_test(index))
    321355
    322356    def display_login_form(self, request, error_message='', extra_context=None):
    323357        request.session.set_test_cookie()
    class AdminSite(object):  
    377411        return render_to_response(self.app_index_template or 'admin/app_index.html', context,
    378412            context_instance=template.RequestContext(request)
    379413        )
     414    app_index = admin_perm_test(app_index)
    380415
    381416# This global object represents the default admin site, for the common case.
    382417# You can instantiate AdminSite in your own code to create a custom admin site.
  • django/contrib/admin/util.py

    diff --git a/django/contrib/admin/util.py b/django/contrib/admin/util.py
    index 17213ea..2e184d9 100644
    a b from django.utils.text import capfirst  
    66from django.utils.encoding import force_unicode
    77from django.utils.translation import ugettext as _
    88
     9def admin_perm_test(func):
     10    def inner(admin_site_or_modeladmin, request, *args, **kwargs):
     11        if hasattr(admin_site_or_modeladmin, 'has_permission'):
     12            admin_site = admin_site_or_modeladmin
     13        else:
     14            admin_site = admin_site_or_modeladmin.admin_site
     15        if not admin_site.has_permission(request):
     16            return admin_site.login(request)
     17        # User has right permisssions show the view
     18        return func(admin_site_or_modeladmin, request, *args, **kwargs)
     19    return inner
    920
    1021def quote(s):
    1122    """
  • django/core/urlresolvers.py

    diff --git a/django/core/urlresolvers.py b/django/core/urlresolvers.py
    index 774e6d3..897cde2 100644
    a b class RegexURLResolver(object):  
    143143        # urlconf_name is a string representing the module containing urlconfs.
    144144        self.regex = re.compile(regex, re.UNICODE)
    145145        self.urlconf_name = urlconf_name
     146        if not isinstance(self.urlconf_name, basestring):
     147            self._urlconf_module = self.urlconf_name
    146148        self.callback = None
    147149        self.default_kwargs = default_kwargs or {}
    148150        self._reverse_dict = MultiValueDict()
  • docs/intro/tutorial02.txt

    diff --git a/docs/intro/tutorial02.txt b/docs/intro/tutorial02.txt
    index b8cc1a0..48c2150 100644
    a b activate the admin site for your installation, do these three things:  
    5757              # (r'^admin/doc/', include('django.contrib.admindocs.urls')),
    5858
    5959              # Uncomment the next line to enable the admin:
    60               **(r'^admin/(.*)', admin.site.root),**
     60              **(r'^admin/', include(admin.site.urls)),**
    6161          )
    6262
    6363      (The bold lines are the ones that needed to be uncommented.)
  • docs/ref/contrib/admin.txt

    diff --git a/docs/ref/contrib/admin.txt b/docs/ref/contrib/admin.txt
    index 4f3f898..1ae78fd 100644
    a b In this example, we register the default ``AdminSite`` instance  
    10271027    admin.autodiscover()
    10281028
    10291029    urlpatterns = patterns('',
    1030         ('^admin/(.*)', admin.site.root),
     1030        ('^admin/', include(admin.site.urls)),
    10311031    )
    10321032
    10331033Above we used ``admin.autodiscover()`` to automatically load the
    In this example, we register the ``AdminSite`` instance  
    10411041    from myproject.admin import admin_site
    10421042
    10431043    urlpatterns = patterns('',
    1044         ('^myadmin/(.*)', admin_site.root),
     1044        ('^myadmin/', include(admin_site.urls)),
    10451045    )
    10461046
    10471047There is really no need to use autodiscover when using your own ``AdminSite``
    10481048instance since you will likely be importing all the per-app admin.py modules
    10491049in your ``myproject.admin`` module.
    10501050
    1051 Note that the regular expression in the URLpattern *must* group everything in
    1052 the URL that comes after the URL root -- hence the ``(.*)`` in these examples.
    10531051
    10541052Multiple admin sites in the same URLconf
    10551053----------------------------------------
    respectively::  
    10681066    from myproject.admin import basic_site, advanced_site
    10691067
    10701068    urlpatterns = patterns('',
    1071         ('^basic-admin/(.*)', basic_site.root),
    1072         ('^advanced-admin/(.*)', advanced_site.root),
     1069        ('^basic-admin/', include(basic_site.root)),
     1070        ('^advanced-admin/', include(advanced_site.root)),
    10731071    )
  • tests/regressiontests/admin_views/tests.py

    diff --git a/tests/regressiontests/admin_views/tests.py b/tests/regressiontests/admin_views/tests.py
    index 5a10971..2d12360 100644
    a b class AdminViewBasicTest(TestCase):  
    2626        """
    2727        request = self.client.get('/test_admin/admin/admin_views/article/add')
    2828        self.assertRedirects(request,
    29             '/test_admin/admin/admin_views/article/add/'
     29            '/test_admin/admin/admin_views/article/add/', status_code=301
    3030        )
    3131   
    3232    def testBasicAddGet(self):
  • tests/regressiontests/admin_views/urls.py

    diff --git a/tests/regressiontests/admin_views/urls.py b/tests/regressiontests/admin_views/urls.py
    index 4e5da48..02e0286 100644
    a b import views  
    55urlpatterns = patterns('',
    66    (r'^admin/doc/', include('django.contrib.admindocs.urls')),
    77    (r'^admin/secure-view/$', views.secure_view),
    8     (r'^admin/(.*)', admin.site.root),
     8    (r'^admin/', include(admin.site.urls)),
    99)
Back to Top