PageRenderTime 393ms CodeModel.GetById 120ms app.highlight 90ms RepoModel.GetById 112ms app.codeStats 0ms

/django/contrib/admin/sites.py

https://code.google.com/p/mango-py/
Python | 432 lines | 371 code | 13 blank | 48 comment | 20 complexity | 511091bddd26f1ae2d7cf39b4df6cd97 MD5 | raw file
  1import re
  2from django import http, template
  3from django.contrib.admin import ModelAdmin, actions
  4from django.contrib.admin.forms import AdminAuthenticationForm
  5from django.contrib.auth import REDIRECT_FIELD_NAME
  6from django.contrib.contenttypes import views as contenttype_views
  7from django.views.decorators.csrf import csrf_protect
  8from django.db.models.base import ModelBase
  9from django.core.exceptions import ImproperlyConfigured
 10from django.core.urlresolvers import reverse
 11from django.shortcuts import render_to_response
 12from django.utils.functional import update_wrapper
 13from django.utils.safestring import mark_safe
 14from django.utils.text import capfirst
 15from django.utils.translation import ugettext as _
 16from django.views.decorators.cache import never_cache
 17from django.conf import settings
 18
 19LOGIN_FORM_KEY = 'this_is_the_login_form'
 20
 21class AlreadyRegistered(Exception):
 22    pass
 23
 24class NotRegistered(Exception):
 25    pass
 26
 27class AdminSite(object):
 28    """
 29    An AdminSite object encapsulates an instance of the Django admin application, ready
 30    to be hooked in to your URLconf. Models are registered with the AdminSite using the
 31    register() method, and the get_urls() method can then be used to access Django view
 32    functions that present a full admin interface for the collection of registered
 33    models.
 34    """
 35    login_form = None
 36    index_template = None
 37    app_index_template = None
 38    login_template = None
 39    logout_template = None
 40    password_change_template = None
 41    password_change_done_template = None
 42
 43    def __init__(self, name=None, app_name='admin'):
 44        self._registry = {} # model_class class -> admin_class instance
 45        self.root_path = None
 46        if name is None:
 47            self.name = 'admin'
 48        else:
 49            self.name = name
 50        self.app_name = app_name
 51        self._actions = {'delete_selected': actions.delete_selected}
 52        self._global_actions = self._actions.copy()
 53
 54    def register(self, model_or_iterable, admin_class=None, **options):
 55        """
 56        Registers the given model(s) with the given admin class.
 57
 58        The model(s) should be Model classes, not instances.
 59
 60        If an admin class isn't given, it will use ModelAdmin (the default
 61        admin options). If keyword arguments are given -- e.g., list_display --
 62        they'll be applied as options to the admin class.
 63
 64        If a model is already registered, this will raise AlreadyRegistered.
 65
 66        If a model is abstract, this will raise ImproperlyConfigured.
 67        """
 68        if not admin_class:
 69            admin_class = ModelAdmin
 70
 71        # Don't import the humongous validation code unless required
 72        if admin_class and settings.DEBUG:
 73            from django.contrib.admin.validation import validate
 74        else:
 75            validate = lambda model, adminclass: None
 76
 77        if isinstance(model_or_iterable, ModelBase):
 78            model_or_iterable = [model_or_iterable]
 79        for model in model_or_iterable:
 80            if model._meta.abstract:
 81                raise ImproperlyConfigured('The model %s is abstract, so it '
 82                      'cannot be registered with admin.' % model.__name__)
 83
 84            if model in self._registry:
 85                raise AlreadyRegistered('The model %s is already registered' % model.__name__)
 86
 87            # If we got **options then dynamically construct a subclass of
 88            # admin_class with those **options.
 89            if options:
 90                # For reasons I don't quite understand, without a __module__
 91                # the created class appears to "live" in the wrong place,
 92                # which causes issues later on.
 93                options['__module__'] = __name__
 94                admin_class = type("%sAdmin" % model.__name__, (admin_class,), options)
 95
 96            # Validate (which might be a no-op)
 97            validate(admin_class, model)
 98
 99            # Instantiate the admin class to save in the registry
100            self._registry[model] = admin_class(model, self)
101
102    def unregister(self, model_or_iterable):
103        """
104        Unregisters the given model(s).
105
106        If a model isn't already registered, this will raise NotRegistered.
107        """
108        if isinstance(model_or_iterable, ModelBase):
109            model_or_iterable = [model_or_iterable]
110        for model in model_or_iterable:
111            if model not in self._registry:
112                raise NotRegistered('The model %s is not registered' % model.__name__)
113            del self._registry[model]
114
115    def add_action(self, action, name=None):
116        """
117        Register an action to be available globally.
118        """
119        name = name or action.__name__
120        self._actions[name] = action
121        self._global_actions[name] = action
122
123    def disable_action(self, name):
124        """
125        Disable a globally-registered action. Raises KeyError for invalid names.
126        """
127        del self._actions[name]
128
129    def get_action(self, name):
130        """
131        Explicitally get a registered global action wheather it's enabled or
132        not. Raises KeyError for invalid names.
133        """
134        return self._global_actions[name]
135
136    @property
137    def actions(self):
138        """
139        Get all the enabled actions as an iterable of (name, func).
140        """
141        return self._actions.iteritems()
142
143    def has_permission(self, request):
144        """
145        Returns True if the given HttpRequest has permission to view
146        *at least one* page in the admin site.
147        """
148        return request.user.is_active and request.user.is_staff
149
150    def check_dependencies(self):
151        """
152        Check that all things needed to run the admin have been correctly installed.
153
154        The default implementation checks that LogEntry, ContentType and the
155        auth context processor are installed.
156        """
157        from django.contrib.admin.models import LogEntry
158        from django.contrib.contenttypes.models import ContentType
159
160        if not LogEntry._meta.installed:
161            raise ImproperlyConfigured("Put 'django.contrib.admin' in your "
162                "INSTALLED_APPS setting in order to use the admin application.")
163        if not ContentType._meta.installed:
164            raise ImproperlyConfigured("Put 'django.contrib.contenttypes' in "
165                "your INSTALLED_APPS setting in order to use the admin application.")
166        if not ('django.contrib.auth.context_processors.auth' in settings.TEMPLATE_CONTEXT_PROCESSORS or
167            'django.core.context_processors.auth' in settings.TEMPLATE_CONTEXT_PROCESSORS):
168            raise ImproperlyConfigured("Put 'django.contrib.auth.context_processors.auth' "
169                "in your TEMPLATE_CONTEXT_PROCESSORS setting in order to use the admin application.")
170
171    def admin_view(self, view, cacheable=False):
172        """
173        Decorator to create an admin view attached to this ``AdminSite``. This
174        wraps the view and provides permission checking by calling
175        ``self.has_permission``.
176
177        You'll want to use this from within ``AdminSite.get_urls()``:
178
179            class MyAdminSite(AdminSite):
180
181                def get_urls(self):
182                    from django.conf.urls.defaults import patterns, url
183
184                    urls = super(MyAdminSite, self).get_urls()
185                    urls += patterns('',
186                        url(r'^my_view/$', self.admin_view(some_view))
187                    )
188                    return urls
189
190        By default, admin_views are marked non-cacheable using the
191        ``never_cache`` decorator. If the view can be safely cached, set
192        cacheable=True.
193        """
194        def inner(request, *args, **kwargs):
195            if not self.has_permission(request):
196                return self.login(request)
197            return view(request, *args, **kwargs)
198        if not cacheable:
199            inner = never_cache(inner)
200        # We add csrf_protect here so this function can be used as a utility
201        # function for any view, without having to repeat 'csrf_protect'.
202        if not getattr(view, 'csrf_exempt', False):
203            inner = csrf_protect(inner)
204        return update_wrapper(inner, view)
205
206    def get_urls(self):
207        from django.conf.urls.defaults import patterns, url, include
208
209        if settings.DEBUG:
210            self.check_dependencies()
211
212        def wrap(view, cacheable=False):
213            def wrapper(*args, **kwargs):
214                return self.admin_view(view, cacheable)(*args, **kwargs)
215            return update_wrapper(wrapper, view)
216
217        # Admin-site-wide views.
218        urlpatterns = patterns('',
219            url(r'^$',
220                wrap(self.index),
221                name='index'),
222            url(r'^logout/$',
223                wrap(self.logout),
224                name='logout'),
225            url(r'^password_change/$',
226                wrap(self.password_change, cacheable=True),
227                name='password_change'),
228            url(r'^password_change/done/$',
229                wrap(self.password_change_done, cacheable=True),
230                name='password_change_done'),
231            url(r'^jsi18n/$',
232                wrap(self.i18n_javascript, cacheable=True),
233                name='jsi18n'),
234            url(r'^r/(?P<content_type_id>\d+)/(?P<object_id>.+)/$',
235                wrap(contenttype_views.shortcut)),
236            url(r'^(?P<app_label>\w+)/$',
237                wrap(self.app_index),
238                name='app_list')
239        )
240
241        # Add in each model's views.
242        for model, model_admin in self._registry.iteritems():
243            urlpatterns += patterns('',
244                url(r'^%s/%s/' % (model._meta.app_label, model._meta.module_name),
245                    include(model_admin.urls))
246            )
247        return urlpatterns
248
249    @property
250    def urls(self):
251        return self.get_urls(), self.app_name, self.name
252
253    def password_change(self, request):
254        """
255        Handles the "change password" task -- both form display and validation.
256        """
257        from django.contrib.auth.views import password_change
258        if self.root_path is not None:
259            url = '%spassword_change/done/' % self.root_path
260        else:
261            url = reverse('admin:password_change_done', current_app=self.name)
262        defaults = {
263            'current_app': self.name,
264            'post_change_redirect': url
265        }
266        if self.password_change_template is not None:
267            defaults['template_name'] = self.password_change_template
268        return password_change(request, **defaults)
269
270    def password_change_done(self, request, extra_context=None):
271        """
272        Displays the "success" page after a password change.
273        """
274        from django.contrib.auth.views import password_change_done
275        defaults = {
276            'current_app': self.name,
277            'extra_context': extra_context or {},
278        }
279        if self.password_change_done_template is not None:
280            defaults['template_name'] = self.password_change_done_template
281        return password_change_done(request, **defaults)
282
283    def i18n_javascript(self, request):
284        """
285        Displays the i18n JavaScript that the Django admin requires.
286
287        This takes into account the USE_I18N setting. If it's set to False, the
288        generated JavaScript will be leaner and faster.
289        """
290        if settings.USE_I18N:
291            from django.views.i18n import javascript_catalog
292        else:
293            from django.views.i18n import null_javascript_catalog as javascript_catalog
294        return javascript_catalog(request, packages=['django.conf', 'django.contrib.admin'])
295
296    @never_cache
297    def logout(self, request, extra_context=None):
298        """
299        Logs out the user for the given HttpRequest.
300
301        This should *not* assume the user is already logged in.
302        """
303        from django.contrib.auth.views import logout
304        defaults = {
305            'current_app': self.name,
306            'extra_context': extra_context or {},
307        }
308        if self.logout_template is not None:
309            defaults['template_name'] = self.logout_template
310        return logout(request, **defaults)
311
312    @never_cache
313    def login(self, request, extra_context=None):
314        """
315        Displays the login form for the given HttpRequest.
316        """
317        from django.contrib.auth.views import login
318        context = {
319            'title': _('Log in'),
320            'root_path': self.root_path,
321            'app_path': request.get_full_path(),
322            REDIRECT_FIELD_NAME: request.get_full_path(),
323        }
324        context.update(extra_context or {})
325        defaults = {
326            'extra_context': context,
327            'current_app': self.name,
328            'authentication_form': self.login_form or AdminAuthenticationForm,
329            'template_name': self.login_template or 'admin/login.html',
330        }
331        return login(request, **defaults)
332
333    @never_cache
334    def index(self, request, extra_context=None):
335        """
336        Displays the main admin index page, which lists all of the installed
337        apps that have been registered in this site.
338        """
339        app_dict = {}
340        user = request.user
341        for model, model_admin in self._registry.items():
342            app_label = model._meta.app_label
343            has_module_perms = user.has_module_perms(app_label)
344
345            if has_module_perms:
346                perms = model_admin.get_model_perms(request)
347
348                # Check whether user has any perm for this module.
349                # If so, add the module to the model_list.
350                if True in perms.values():
351                    model_dict = {
352                        'name': capfirst(model._meta.verbose_name_plural),
353                        'admin_url': mark_safe('%s/%s/' % (app_label, model.__name__.lower())),
354                        'perms': perms,
355                    }
356                    if app_label in app_dict:
357                        app_dict[app_label]['models'].append(model_dict)
358                    else:
359                        app_dict[app_label] = {
360                            'name': app_label.title(),
361                            'app_url': app_label + '/',
362                            'has_module_perms': has_module_perms,
363                            'models': [model_dict],
364                        }
365
366        # Sort the apps alphabetically.
367        app_list = app_dict.values()
368        app_list.sort(key=lambda x: x['name'])
369
370        # Sort the models alphabetically within each app.
371        for app in app_list:
372            app['models'].sort(key=lambda x: x['name'])
373
374        context = {
375            'title': _('Site administration'),
376            'app_list': app_list,
377            'root_path': self.root_path,
378        }
379        context.update(extra_context or {})
380        context_instance = template.RequestContext(request, current_app=self.name)
381        return render_to_response(self.index_template or 'admin/index.html', context,
382            context_instance=context_instance
383        )
384
385    def app_index(self, request, app_label, extra_context=None):
386        user = request.user
387        has_module_perms = user.has_module_perms(app_label)
388        app_dict = {}
389        for model, model_admin in self._registry.items():
390            if app_label == model._meta.app_label:
391                if has_module_perms:
392                    perms = model_admin.get_model_perms(request)
393
394                    # Check whether user has any perm for this module.
395                    # If so, add the module to the model_list.
396                    if True in perms.values():
397                        model_dict = {
398                            'name': capfirst(model._meta.verbose_name_plural),
399                            'admin_url': '%s/' % model.__name__.lower(),
400                            'perms': perms,
401                        }
402                        if app_dict:
403                            app_dict['models'].append(model_dict),
404                        else:
405                            # First time around, now that we know there's
406                            # something to display, add in the necessary meta
407                            # information.
408                            app_dict = {
409                                'name': app_label.title(),
410                                'app_url': '',
411                                'has_module_perms': has_module_perms,
412                                'models': [model_dict],
413                            }
414        if not app_dict:
415            raise http.Http404('The requested admin page does not exist.')
416        # Sort the models alphabetically within each app.
417        app_dict['models'].sort(key=lambda x: x['name'])
418        context = {
419            'title': _('%s administration') % capfirst(app_label),
420            'app_list': [app_dict],
421            'root_path': self.root_path,
422        }
423        context.update(extra_context or {})
424        context_instance = template.RequestContext(request, current_app=self.name)
425        return render_to_response(self.app_index_template or ('admin/%s/app_index.html' % app_label,
426            'admin/app_index.html'), context,
427            context_instance=context_instance
428        )
429
430# This global object represents the default admin site, for the common case.
431# You can instantiate AdminSite in your own code to create a custom admin site.
432site = AdminSite()