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