1
from django import forms, template
2
from django.conf import settings
3
from django.contrib.admin.filterspecs import FilterSpec
2
4
from django.contrib.admin.views.decorators import staff_member_required
3
from django.contrib.admin.filterspecs import FilterSpec
4
from django.core import formfields, meta, template
5
from django.core.template import loader
6
from django.core.meta.fields import BoundField, BoundFieldLine, BoundFieldSet
7
from django.core.exceptions import Http404, ImproperlyConfigured, ObjectDoesNotExist, PermissionDenied
8
from django.core.extensions import DjangoContext as Context
9
from django.core.extensions import get_object_or_404, render_to_response
5
from django.views.decorators.cache import never_cache
6
from django.contrib.contenttypes.models import ContentType
7
from django.core.exceptions import ImproperlyConfigured, ObjectDoesNotExist, PermissionDenied
10
8
from django.core.paginator import ObjectPaginator, InvalidPage
11
from django.conf.settings import ADMIN_MEDIA_PREFIX
13
from django.models.admin import log
15
raise ImproperlyConfigured, "You don't have 'django.contrib.admin' in INSTALLED_APPS."
16
from django.utils.html import escape
17
from django.utils.httpwrappers import HttpResponse, HttpResponseRedirect
18
from django.utils.text import capfirst, get_text_list
9
from django.shortcuts import get_object_or_404, render_to_response
10
from django.db import models
11
from django.db.models.query import handle_legacy_orderlist, QuerySet
12
from django.http import Http404, HttpResponse, HttpResponseRedirect
13
from django.template import loader
19
14
from django.utils import dateformat
20
15
from django.utils.dates import MONTHS
21
16
from django.utils.html import escape
17
from django.utils.text import capfirst, get_text_list
24
# The system will display a "Show all" link only if the total result count
25
# is less than or equal to this setting.
20
from django.contrib.admin.models import LogEntry, ADDITION, CHANGE, DELETION
21
if not LogEntry._meta.installed:
22
raise ImproperlyConfigured, "You'll need to put 'django.contrib.admin' in your INSTALLED_APPS setting before you can use the admin application."
24
if 'django.core.context_processors.auth' not in settings.TEMPLATE_CONTEXT_PROCESSORS:
25
raise ImproperlyConfigured, "You'll need to put 'django.core.context_processors.auth' in your TEMPLATE_CONTEXT_PROCESSORS setting before you can use the admin application."
27
# The system will display a "Show all" link on the change list only if the
28
# total result count is less than or equal to this setting.
26
29
MAX_SHOW_ALL_ALLOWED = 200
28
DEFAULT_RESULTS_PER_PAGE = 100
32
34
ORDER_TYPE_VAR = 'ot'
35
37
IS_POPUP_VAR = 'pop'
37
# Text to display within changelist table cells if the value is blank.
39
# Text to display within change-list table cells if the value is blank.
38
40
EMPTY_CHANGELIST_VALUE = '(None)'
40
def _get_mod_opts(app_label, module_name):
41
"Helper function that returns a tuple of (module, opts), raising Http404 if necessary."
43
mod = meta.get_module(app_label, module_name)
45
raise Http404 # Invalid app or module name. Maybe it's not in INSTALLED_APPS.
46
opts = mod.Klass._meta
48
raise Http404 # This object is valid but has no admin interface.
52
return render_to_response('admin/index', {'title': _('Site administration')}, context_instance=Context(request))
53
index = staff_member_required(index)
42
use_raw_id_admin = lambda field: isinstance(field.rel, (models.ManyToOneRel, models.ManyToManyRel)) and field.rel.raw_id_admin
55
44
class IncorrectLookupParameters(Exception):
58
class ChangeList(object):
59
def __init__(self, request, app_label, module_name):
60
self.get_modules_and_options(app_label, module_name, request)
61
self.get_search_parameters(request)
63
self.query = request.GET.get(SEARCH_VAR, '')
64
self.get_lookup_params()
65
self.get_results(request)
66
self.title = (self.is_popup
67
and _('Select %s') % self.opts.verbose_name
68
or _('Select %s to change') % self.opts.verbose_name)
69
self.get_filters(request)
70
self.pk_attname = self.lookup_opts.pk.attname
72
def get_filters(self, request):
73
self.filter_specs = []
74
if self.lookup_opts.admin.list_filter and not self.opts.one_to_one_field:
75
filter_fields = [self.lookup_opts.get_field(field_name) \
76
for field_name in self.lookup_opts.admin.list_filter]
77
for f in filter_fields:
78
spec = FilterSpec.create(f, request, self.params)
79
if spec and spec.has_output():
80
self.filter_specs.append(spec)
81
self.has_filters = bool(self.filter_specs)
83
def get_query_string(self, new_params={}, remove=[]):
84
p = self.params.copy()
89
for k, v in new_params.items():
90
if p.has_key(k) and v is None:
94
return '?' + '&'.join(['%s=%s' % (k, v) for k, v in p.items()]).replace(' ', '%20')
96
def get_modules_and_options(self, app_label, module_name, request):
97
self.mod, self.opts = _get_mod_opts(app_label, module_name)
98
if not request.user.has_perm(app_label + '.' + self.opts.get_change_permission()):
99
raise PermissionDenied
101
self.lookup_mod, self.lookup_opts = self.mod, self.opts
103
def get_search_parameters(self, request):
104
# Get search parameters from the query string.
106
self.page_num = int(request.GET.get(PAGE_VAR, 0))
109
self.show_all = request.GET.has_key(ALL_VAR)
110
self.is_popup = request.GET.has_key(IS_POPUP_VAR)
111
self.params = dict(request.GET.items())
112
if self.params.has_key(PAGE_VAR):
113
del self.params[PAGE_VAR]
115
def get_results(self, request):
116
lookup_mod, lookup_params, show_all, page_num = \
117
self.lookup_mod, self.lookup_params, self.show_all, self.page_num
120
paginator = ObjectPaginator(lookup_mod, lookup_params, DEFAULT_RESULTS_PER_PAGE)
121
# Naked except! Because we don't have any other way of validating "params".
122
# They might be invalid if the keyword arguments are incorrect, or if the
123
# values are not in the correct type (which would result in a database
126
raise IncorrectLookupParameters()
128
# Get the total number of objects, with no filters applied.
129
real_lookup_params = lookup_params.copy()
130
del real_lookup_params['order_by']
131
if real_lookup_params:
132
full_result_count = lookup_mod.get_count()
134
full_result_count = paginator.hits
135
del real_lookup_params
136
result_count = paginator.hits
137
can_show_all = result_count <= MAX_SHOW_ALL_ALLOWED
138
multi_page = result_count > DEFAULT_RESULTS_PER_PAGE
140
# Get the list of objects to display on this page.
141
if (show_all and can_show_all) or not multi_page:
142
result_list = lookup_mod.get_list(**lookup_params)
145
result_list = paginator.get_page(page_num)
148
(self.result_count, self.full_result_count, self.result_list,
149
self.can_show_all, self.multi_page, self.paginator) = (result_count,
150
full_result_count, result_list, can_show_all, multi_page, paginator )
152
def url_for_result(self, result):
153
return "%s/" % getattr(result, self.pk_attname)
155
def get_ordering(self):
156
lookup_opts, params = self.lookup_opts, self.params
157
# For ordering, first check the "ordering" parameter in the admin options,
158
# then check the object's default ordering. If neither of those exist,
159
# order descending by ID by default. Finally, look for manually-specified
160
# ordering from the query string.
161
ordering = lookup_opts.admin.ordering or lookup_opts.ordering or ['-' + lookup_opts.pk.name]
163
# Normalize it to new-style ordering.
164
ordering = meta.handle_legacy_orderlist(ordering)
166
if ordering[0].startswith('-'):
167
order_field, order_type = ordering[0][1:], 'desc'
169
order_field, order_type = ordering[0], 'asc'
170
if params.has_key(ORDER_VAR):
173
f = lookup_opts.get_field(lookup_opts.admin.list_display[int(params[ORDER_VAR])])
174
except meta.FieldDoesNotExist:
177
if not isinstance(f.rel, meta.ManyToOneRel) or not f.null:
179
except (IndexError, ValueError):
180
pass # Invalid ordering specified. Just use the default.
181
if params.has_key(ORDER_TYPE_VAR) and params[ORDER_TYPE_VAR] in ('asc', 'desc'):
182
order_type = params[ORDER_TYPE_VAR]
183
self.order_field, self.order_type = order_field, order_type
185
def get_lookup_params(self):
186
# Prepare the lookup parameters for the API lookup.
187
(params, order_field, lookup_opts, order_type, opts, query) = \
188
(self.params, self.order_field, self.lookup_opts, self.order_type, self.opts, self.query)
190
lookup_params = params.copy()
191
for i in (ALL_VAR, ORDER_VAR, ORDER_TYPE_VAR, SEARCH_VAR, IS_POPUP_VAR):
192
if lookup_params.has_key(i):
194
# If the order-by field is a field with a relationship, order by the value
195
# in the related table.
196
lookup_order_field = order_field
198
f = lookup_opts.get_field(order_field)
199
except meta.FieldDoesNotExist:
202
if isinstance(lookup_opts.get_field(order_field).rel, meta.ManyToOneRel):
203
f = lookup_opts.get_field(order_field)
204
rel_ordering = f.rel.to.ordering and f.rel.to.ordering[0] or f.rel.to.pk.column
205
lookup_order_field = '%s.%s' % (f.rel.to.db_table, rel_ordering)
206
# Use select_related if one of the list_display options is a field with a
208
if lookup_opts.admin.list_select_related:
209
lookup_params['select_related'] = True
211
for field_name in lookup_opts.admin.list_display:
213
f = lookup_opts.get_field(field_name)
214
except meta.FieldDoesNotExist:
217
if isinstance(f.rel, meta.ManyToOneRel):
218
lookup_params['select_related'] = True
220
lookup_params['order_by'] = ((order_type == 'desc' and '-' or '') + lookup_order_field,)
221
if lookup_opts.admin.search_fields and query:
223
for bit in query.split():
225
for field_name in lookup_opts.admin.search_fields:
226
or_queries.append(meta.Q(**{'%s__icontains' % field_name: bit}))
227
complex_queries.append(reduce(operator.or_, or_queries))
228
lookup_params['complex'] = reduce(operator.and_, complex_queries)
229
if opts.one_to_one_field:
230
lookup_params.update(opts.one_to_one_field.rel.limit_choices_to)
231
self.lookup_params = lookup_params
233
def change_list(request, app_label, module_name):
235
cl = ChangeList(request, app_label, module_name)
236
except IncorrectLookupParameters:
237
return HttpResponseRedirect(request.path)
239
c = Context(request, {
241
'is_popup': cl.is_popup,
244
c.update({'has_add_permission': c['perms'][app_label][cl.opts.get_add_permission()]}),
245
return render_to_response(['admin/%s/%s/change_list' % (app_label, cl.opts.object_name.lower()),
246
'admin/%s/change_list' % app_label,
247
'admin/change_list'], context_instance=c)
248
change_list = staff_member_required(change_list)
250
use_raw_id_admin = lambda field: isinstance(field.rel, (meta.ManyToOneRel, meta.ManyToManyRel)) and field.rel.raw_id_admin
252
def get_javascript_imports(opts,auto_populated_fields, ordered_objects, field_sets):
49
Ensure that primary key values do not confuse the admin URLs by escaping
50
any '/', '_' and ':' characters. Similar to urllib.quote, except that the
51
quoting is slightly different so that it doesn't get autoamtically
52
unquoted by the web browser.
54
if type(s) != type(''):
57
for i in range(len(res)):
60
res[i] = '_%02X' % ord(c)
65
Undo the effects of quote(). Based heavily on urllib.unquote().
76
myappend(mychr(myatoi(item[:2], 16))
84
def get_javascript_imports(opts, auto_populated_fields, field_sets):
253
85
# Put in any necessary JavaScript imports.
254
86
js = ['js/core.js', 'js/admin/RelatedObjectLookups.js']
255
87
if auto_populated_fields:
256
88
js.append('js/urlify.js')
257
if opts.has_field_type(meta.DateTimeField) or opts.has_field_type(meta.TimeField) or opts.has_field_type(meta.DateField):
89
if opts.has_field_type(models.DateTimeField) or opts.has_field_type(models.TimeField) or opts.has_field_type(models.DateField):
258
90
js.extend(['js/calendar.js', 'js/admin/DateTimeShortcuts.js'])
91
if opts.get_ordered_objects():
260
92
js.extend(['js/getElementsBySelector.js', 'js/dom-drag.js' , 'js/admin/ordering.js'])
262
94
js.extend(opts.admin.js)
325
154
def html_error_list(self):
326
155
return " ".join([form_field.html_error_list() for form_field in self.form_fields if form_field.errors])
328
class AdminBoundFieldLine(BoundFieldLine):
157
def original_url(self):
158
if self.is_file_field and self.original and self.field.attname:
159
url_method = getattr(self.original, 'get_%s_url' % self.field.attname)
160
if callable(url_method):
164
class AdminBoundFieldLine(object):
329
165
def __init__(self, field_line, field_mapping, original):
330
super(AdminBoundFieldLine, self).__init__(field_line, field_mapping, original, AdminBoundField)
166
self.bound_fields = [field.bind(field_mapping, original, AdminBoundField) for field in field_line]
331
167
for bound_field in self:
332
168
bound_field.first = True
335
class AdminBoundFieldSet(BoundFieldSet):
172
for bound_field in self.bound_fields:
176
return len(self.bound_fields)
178
class AdminBoundFieldSet(object):
336
179
def __init__(self, field_set, field_mapping, original):
337
super(AdminBoundFieldSet, self).__init__(field_set, field_mapping, original, AdminBoundFieldLine)
339
class BoundManipulator(object):
340
def __init__(self, opts, manipulator, field_mapping):
341
self.inline_related_objects = opts.get_followed_related_objects(manipulator.follow)
342
self.original = hasattr(manipulator, 'original_object') and manipulator.original_object or None
343
self.bound_field_sets = [field_set.bind(field_mapping, self.original, AdminBoundFieldSet)
344
for field_set in opts.admin.get_field_sets(opts)]
345
self.ordered_objects = opts.get_ordered_objects()[:]
347
class AdminBoundManipulator(BoundManipulator):
348
def __init__(self, opts, manipulator, field_mapping):
349
super(AdminBoundManipulator, self).__init__(opts, manipulator, field_mapping)
350
field_sets = opts.admin.get_field_sets(opts)
352
self.auto_populated_fields = [f for f in opts.fields if f.prepopulate_from]
353
self.javascript_imports = get_javascript_imports(opts, self.auto_populated_fields, self.ordered_objects, field_sets);
355
self.coltype = self.ordered_objects and 'colMS' or 'colM'
356
self.has_absolute_url = hasattr(opts.get_model_module().Klass, 'get_absolute_url')
357
self.form_enc_attrib = opts.has_field_type(meta.FileField) and \
358
'enctype="multipart/form-data" ' or ''
360
self.first_form_field_id = self.bound_field_sets[0].bound_field_lines[0].bound_fields[0].form_fields[0].get_id();
361
self.ordered_object_pk_names = [o.pk.name for o in self.ordered_objects]
363
self.save_on_top = opts.admin.save_on_top
364
self.save_as = opts.admin.save_as
366
self.content_type_id = opts.get_content_type_id()
367
self.verbose_name_plural = opts.verbose_name_plural
368
self.verbose_name = opts.verbose_name
369
self.object_name = opts.object_name
371
def get_ordered_object_pk(self, ordered_obj):
372
for name in self.ordered_object_pk_names:
373
if hasattr(ordered_obj, name):
374
return str(getattr(ordered_obj, name))
377
def render_change_form(opts, manipulator, app_label, context, add=False, change=False, show_delete=False, form_url=''):
180
self.name = field_set.name
181
self.classes = field_set.classes
182
self.description = field_set.description
183
self.bound_field_lines = [field_line.bind(field_mapping, original, AdminBoundFieldLine) for field_line in field_set]
186
for bound_field_line in self.bound_field_lines:
187
yield bound_field_line
190
return len(self.bound_field_lines)
192
def render_change_form(model, manipulator, context, add=False, change=False, form_url=''):
194
app_label = opts.app_label
195
auto_populated_fields = [f for f in opts.fields if f.prepopulate_from]
196
field_sets = opts.admin.get_field_sets(opts)
197
original = getattr(manipulator, 'original_object', None)
198
bound_field_sets = [field_set.bind(context['form'], original, AdminBoundFieldSet) for field_set in field_sets]
199
first_form_field_id = bound_field_sets[0].bound_field_lines[0].bound_fields[0].form_fields[0].get_id();
200
ordered_objects = opts.get_ordered_objects()
201
inline_related_objects = opts.get_followed_related_objects(manipulator.follow)
378
202
extra_context = {
380
204
'change': change,
381
'bound_manipulator': AdminBoundManipulator(opts, manipulator, context['form']),
382
205
'has_delete_permission': context['perms'][app_label][opts.get_delete_permission()],
206
'has_change_permission': context['perms'][app_label][opts.get_change_permission()],
207
'has_file_field': opts.has_field_type(models.FileField),
208
'has_absolute_url': hasattr(model, 'get_absolute_url'),
209
'auto_populated_fields': auto_populated_fields,
210
'bound_field_sets': bound_field_sets,
211
'first_form_field_id': first_form_field_id,
212
'javascript_imports': get_javascript_imports(opts, auto_populated_fields, field_sets),
213
'ordered_objects': ordered_objects,
214
'inline_related_objects': inline_related_objects,
383
215
'form_url': form_url,
384
'app_label': app_label,
217
'content_type_id': ContentType.objects.get_for_model(model).id,
386
219
context.update(extra_context)
387
return render_to_response(["admin/%s/%s/change_form" % (app_label, opts.object_name.lower() ),
388
"admin/%s/change_form" % app_label ,
389
"admin/change_form"], context_instance=context)
391
def log_add_message(user, opts,manipulator,new_object):
392
pk_value = getattr(new_object, opts.pk.attname)
393
log.log_action(user.id, opts.get_content_type_id(), pk_value, str(new_object), log.ADDITION)
395
def add_stage(request, app_label, module_name, show_delete=False, form_url='', post_url='../', post_url_continue='../%s/', object_id_override=None):
396
mod, opts = _get_mod_opts(app_label, module_name)
220
return render_to_response([
221
"admin/%s/%s/change_form.html" % (app_label, opts.object_name.lower()),
222
"admin/%s/change_form.html" % app_label,
223
"admin/change_form.html"], context_instance=context)
226
return render_to_response('admin/index.html', {'title': _('Site administration')}, context_instance=template.RequestContext(request))
227
index = staff_member_required(never_cache(index))
229
def add_stage(request, app_label, model_name, show_delete=False, form_url='', post_url=None, post_url_continue='../%s/', object_id_override=None):
230
model = models.get_model(app_label, model_name)
232
raise Http404, "App %r, model %r, not found" % (app_label, model_name)
397
235
if not request.user.has_perm(app_label + '.' + opts.get_add_permission()):
398
236
raise PermissionDenied
399
manipulator = mod.AddManipulator()
239
if request.user.has_perm(app_label + '.' + opts.get_change_permission()):
240
# redirect to list view
243
# Object list will give 'Permission Denied', so go back to admin home
244
post_url = '../../../'
246
manipulator = model.AddManipulator()
401
248
new_data = request.POST.copy()
402
if opts.has_field_type(meta.FileField):
250
if opts.has_field_type(models.FileField):
403
251
new_data.update(request.FILES)
404
253
errors = manipulator.get_validation_errors(new_data)
405
254
manipulator.do_html2python(new_data)
407
if not errors and not request.POST.has_key("_preview"):
408
257
new_object = manipulator.save(new_data)
409
log_add_message(request.user, opts,manipulator,new_object)
410
msg = _('The %(name)s "%(obj)s" was added successfully.') % {'name':opts.verbose_name, 'obj':new_object}
411
pk_value = getattr(new_object,opts.pk.attname)
258
pk_value = new_object._get_pk_val()
259
LogEntry.objects.log_action(request.user.id, ContentType.objects.get_for_model(model).id, pk_value, str(new_object), ADDITION)
260
msg = _('The %(name)s "%(obj)s" was added successfully.') % {'name': opts.verbose_name, 'obj': new_object}
412
261
# Here, we distinguish between different save types by checking for
413
262
# the presence of keys in request.POST.
414
263
if request.POST.has_key("_continue"):
415
request.user.add_message(msg + ' ' + _("You may edit it again below."))
264
request.user.message_set.create(message=msg + ' ' + _("You may edit it again below."))
416
265
if request.POST.has_key("_popup"):
417
266
post_url_continue += "?_popup=1"
418
267
return HttpResponseRedirect(post_url_continue % pk_value)
437
286
# Populate the FormWrapper.
438
form = formfields.FormWrapper(manipulator, new_data, errors, edit_inline=True)
287
form = forms.FormWrapper(manipulator, new_data, errors)
440
c = Context(request, {
289
c = template.RequestContext(request, {
441
290
'title': _('Add %s') % opts.verbose_name,
443
292
'is_popup': request.REQUEST.has_key('_popup'),
444
293
'show_delete': show_delete,
446
296
if object_id_override is not None:
447
297
c['object_id'] = object_id_override
449
return render_change_form(opts, manipulator, app_label, c, add=True)
450
add_stage = staff_member_required(add_stage)
452
def log_change_message(user, opts,manipulator,new_object):
453
pk_value = getattr(new_object, opts.pk.column)
454
# Construct the change message.
456
if manipulator.fields_added:
457
change_message.append(_('Added %s.') % get_text_list(manipulator.fields_added, _('and')))
458
if manipulator.fields_changed:
459
change_message.append(_('Changed %s.') % get_text_list(manipulator.fields_changed, _('and')))
460
if manipulator.fields_deleted:
461
change_message.append(_('Deleted %s.') % get_text_list(manipulator.fields_deleted, _('and')))
462
change_message = ' '.join(change_message)
463
if not change_message:
464
change_message = _('No fields changed.')
465
log.log_action(user.id, opts.get_content_type_id(), pk_value, str(new_object), log.CHANGE, change_message)
467
def change_stage(request, app_label, module_name, object_id):
468
mod, opts = _get_mod_opts(app_label, module_name)
299
return render_change_form(model, manipulator, c, add=True)
300
add_stage = staff_member_required(never_cache(add_stage))
302
def change_stage(request, app_label, model_name, object_id):
303
model = models.get_model(app_label, model_name)
304
object_id = unquote(object_id)
306
raise Http404, "App %r, model %r, not found" % (app_label, model_name)
469
309
if not request.user.has_perm(app_label + '.' + opts.get_change_permission()):
470
310
raise PermissionDenied
471
312
if request.POST and request.POST.has_key("_saveasnew"):
472
return add_stage(request, app_label, module_name, form_url='../add/')
313
return add_stage(request, app_label, model_name, form_url='../../add/')
474
manipulator = mod.ChangeManipulator(object_id)
316
manipulator = model.ChangeManipulator(object_id)
475
317
except ObjectDoesNotExist:
479
321
new_data = request.POST.copy()
480
if opts.has_field_type(meta.FileField):
323
if opts.has_field_type(models.FileField):
481
324
new_data.update(request.FILES)
483
326
errors = manipulator.get_validation_errors(new_data)
485
327
manipulator.do_html2python(new_data)
486
if not errors and not request.POST.has_key("_preview"):
487
330
new_object = manipulator.save(new_data)
488
log_change_message(request.user,opts,manipulator,new_object)
489
msg = _('The %(name)s "%(obj)s" was changed successfully.') % {'name': opts.verbose_name, 'obj':new_object}
490
pk_value = getattr(new_object,opts.pk.attname)
331
pk_value = new_object._get_pk_val()
333
# Construct the change message.
335
if manipulator.fields_added:
336
change_message.append(_('Added %s.') % get_text_list(manipulator.fields_added, _('and')))
337
if manipulator.fields_changed:
338
change_message.append(_('Changed %s.') % get_text_list(manipulator.fields_changed, _('and')))
339
if manipulator.fields_deleted:
340
change_message.append(_('Deleted %s.') % get_text_list(manipulator.fields_deleted, _('and')))
341
change_message = ' '.join(change_message)
342
if not change_message:
343
change_message = _('No fields changed.')
344
LogEntry.objects.log_action(request.user.id, ContentType.objects.get_for_model(model).id, pk_value, str(new_object), CHANGE, change_message)
346
msg = _('The %(name)s "%(obj)s" was changed successfully.') % {'name': opts.verbose_name, 'obj': new_object}
491
347
if request.POST.has_key("_continue"):
492
request.user.add_message(msg + ' ' + _("You may edit it again below."))
348
request.user.message_set.create(message=msg + ' ' + _("You may edit it again below."))
493
349
if request.REQUEST.has_key('_popup'):
494
350
return HttpResponseRedirect(request.path + "?_popup=1")
496
352
return HttpResponseRedirect(request.path)
497
353
elif request.POST.has_key("_saveasnew"):
498
request.user.add_message(_('The %(name)s "%(obj)s" was added successfully. You may edit it again below.') % {'name': opts.verbose_name, 'obj': new_object})
354
request.user.message_set.create(message=_('The %(name)s "%(obj)s" was added successfully. You may edit it again below.') % {'name': opts.verbose_name, 'obj': new_object})
499
355
return HttpResponseRedirect("../%s/" % pk_value)
500
356
elif request.POST.has_key("_addanother"):
501
request.user.add_message(msg + ' ' + (_("You may add another %s below.") % opts.verbose_name))
357
request.user.message_set.create(message=msg + ' ' + (_("You may add another %s below.") % opts.verbose_name))
502
358
return HttpResponseRedirect("../add/")
504
request.user.add_message(msg)
360
request.user.message_set.create(message=msg)
505
361
return HttpResponseRedirect("../")
507
363
# Populate new_data with a "flattened" version of the current data.
645
504
raise PermissionDenied
646
505
obj_display = str(obj)
648
log.log_action(request.user.id, opts.get_content_type_id(), object_id, obj_display, log.DELETION)
649
request.user.add_message(_('The %(name)s "%(obj)s" was deleted successfully.') % {'name':opts.verbose_name, 'obj':obj_display})
507
LogEntry.objects.log_action(request.user.id, ContentType.objects.get_for_model(model).id, object_id, obj_display, DELETION)
508
request.user.message_set.create(message=_('The %(name)s "%(obj)s" was deleted successfully.') % {'name': opts.verbose_name, 'obj': obj_display})
650
509
return HttpResponseRedirect("../../")
651
return render_to_response('admin/delete_confirmation', {
652
511
"title": _("Are you sure?"),
653
512
"object_name": opts.verbose_name,
655
514
"deleted_objects": deleted_objects,
656
515
"perms_lacking": perms_needed,
657
}, context_instance=Context(request))
658
delete_stage = staff_member_required(delete_stage)
518
return render_to_response(["admin/%s/%s/delete_confirmation.html" % (app_label, opts.object_name.lower() ),
519
"admin/%s/delete_confirmation.html" % app_label ,
520
"admin/delete_confirmation.html"], extra_context, context_instance=template.RequestContext(request))
521
delete_stage = staff_member_required(never_cache(delete_stage))
660
def history(request, app_label, module_name, object_id):
661
mod, opts = _get_mod_opts(app_label, module_name)
662
action_list = log.get_list(object_id__exact=object_id, content_type__id__exact=opts.get_content_type_id(),
663
order_by=("action_time",), select_related=True)
523
def history(request, app_label, model_name, object_id):
524
model = models.get_model(app_label, model_name)
525
object_id = unquote(object_id)
527
raise Http404, "App %r, model %r, not found" % (app_label, model_name)
528
action_list = LogEntry.objects.filter(object_id=object_id,
529
content_type__id__exact=ContentType.objects.get_for_model(model).id).select_related().order_by('action_time')
664
530
# If no history was found, see whether this object even exists.
665
obj = get_object_or_404(mod, pk=object_id)
666
return render_to_response('admin/object_history', {
531
obj = get_object_or_404(model, pk=object_id)
667
533
'title': _('Change history: %s') % obj,
668
534
'action_list': action_list,
669
'module_name': capfirst(opts.verbose_name_plural),
535
'module_name': capfirst(model._meta.verbose_name_plural),
671
}, context_instance=Context(request))
672
history = staff_member_required(history)
538
return render_to_response(["admin/%s/%s/object_history.html" % (app_label, model._meta.object_name.lower()),
539
"admin/%s/object_history.html" % app_label ,
540
"admin/object_history.html"], extra_context, context_instance=template.RequestContext(request))
541
history = staff_member_required(never_cache(history))
543
class ChangeList(object):
544
def __init__(self, request, model):
546
self.opts = model._meta
547
self.lookup_opts = self.opts
548
self.manager = self.opts.admin.manager
550
# Get search parameters from the query string.
552
self.page_num = int(request.GET.get(PAGE_VAR, 0))
555
self.show_all = request.GET.has_key(ALL_VAR)
556
self.is_popup = request.GET.has_key(IS_POPUP_VAR)
557
self.params = dict(request.GET.items())
558
if self.params.has_key(PAGE_VAR):
559
del self.params[PAGE_VAR]
561
self.order_field, self.order_type = self.get_ordering()
562
self.query = request.GET.get(SEARCH_VAR, '')
563
self.query_set = self.get_query_set()
564
self.get_results(request)
565
self.title = (self.is_popup and _('Select %s') % self.opts.verbose_name or _('Select %s to change') % self.opts.verbose_name)
566
self.filter_specs, self.has_filters = self.get_filters(request)
567
self.pk_attname = self.lookup_opts.pk.attname
569
def get_filters(self, request):
571
if self.lookup_opts.admin.list_filter and not self.opts.one_to_one_field:
572
filter_fields = [self.lookup_opts.get_field(field_name) \
573
for field_name in self.lookup_opts.admin.list_filter]
574
for f in filter_fields:
575
spec = FilterSpec.create(f, request, self.params)
576
if spec and spec.has_output():
577
filter_specs.append(spec)
578
return filter_specs, bool(filter_specs)
580
def get_query_string(self, new_params={}, remove=[]):
581
p = self.params.copy()
586
for k, v in new_params.items():
587
if p.has_key(k) and v is None:
591
return '?' + '&'.join(['%s=%s' % (k, v) for k, v in p.items()]).replace(' ', '%20')
593
def get_results(self, request):
594
paginator = ObjectPaginator(self.query_set, self.lookup_opts.admin.list_per_page)
596
# Get the number of objects, with admin filters applied.
598
result_count = paginator.hits
599
# Naked except! Because we don't have any other way of validating
600
# "params". They might be invalid if the keyword arguments are
601
# incorrect, or if the values are not in the correct type (which would
602
# result in a database error).
604
raise IncorrectLookupParameters
606
# Get the total number of objects, with no admin filters applied.
607
# Perform a slight optimization: Check to see whether any filters were
608
# given. If not, use paginator.hits to calculate the number of objects,
609
# because we've already done paginator.hits and the value is cached.
610
if isinstance(self.query_set._filters, models.Q) and not self.query_set._filters.kwargs:
611
full_result_count = result_count
613
full_result_count = self.manager.count()
615
can_show_all = result_count <= MAX_SHOW_ALL_ALLOWED
616
multi_page = result_count > self.lookup_opts.admin.list_per_page
618
# Get the list of objects to display on this page.
619
if (self.show_all and can_show_all) or not multi_page:
620
result_list = list(self.query_set)
623
result_list = paginator.get_page(self.page_num)
627
self.result_count = result_count
628
self.full_result_count = full_result_count
629
self.result_list = result_list
630
self.can_show_all = can_show_all
631
self.multi_page = multi_page
632
self.paginator = paginator
634
def get_ordering(self):
635
lookup_opts, params = self.lookup_opts, self.params
636
# For ordering, first check the "ordering" parameter in the admin options,
637
# then check the object's default ordering. If neither of those exist,
638
# order descending by ID by default. Finally, look for manually-specified
639
# ordering from the query string.
640
ordering = lookup_opts.admin.ordering or lookup_opts.ordering or ['-' + lookup_opts.pk.name]
642
# Normalize it to new-style ordering.
643
ordering = handle_legacy_orderlist(ordering)
645
if ordering[0].startswith('-'):
646
order_field, order_type = ordering[0][1:], 'desc'
648
order_field, order_type = ordering[0], 'asc'
649
if params.has_key(ORDER_VAR):
652
f = lookup_opts.get_field(lookup_opts.admin.list_display[int(params[ORDER_VAR])])
653
except models.FieldDoesNotExist:
656
if not isinstance(f.rel, models.ManyToOneRel) or not f.null:
658
except (IndexError, ValueError):
659
pass # Invalid ordering specified. Just use the default.
660
if params.has_key(ORDER_TYPE_VAR) and params[ORDER_TYPE_VAR] in ('asc', 'desc'):
661
order_type = params[ORDER_TYPE_VAR]
662
return order_field, order_type
664
def get_query_set(self):
665
qs = self.manager.get_query_set()
666
lookup_params = self.params.copy() # a dictionary of the query string
667
for i in (ALL_VAR, ORDER_VAR, ORDER_TYPE_VAR, SEARCH_VAR, IS_POPUP_VAR):
668
if lookup_params.has_key(i):
671
# Apply lookup parameters from the query string.
672
qs = qs.filter(**lookup_params)
674
# Use select_related() if one of the list_display options is a field
675
# with a relationship.
676
if self.lookup_opts.admin.list_select_related:
677
qs = qs.select_related()
679
for field_name in self.lookup_opts.admin.list_display:
681
f = self.lookup_opts.get_field(field_name)
682
except models.FieldDoesNotExist:
685
if isinstance(f.rel, models.ManyToOneRel):
686
qs = qs.select_related()
689
# Calculate lookup_order_field.
690
# If the order-by field is a field with a relationship, order by the
691
# value in the related table.
692
lookup_order_field = self.order_field
694
f = self.lookup_opts.get_field(self.order_field, many_to_many=False)
695
except models.FieldDoesNotExist:
698
if isinstance(f.rel, models.OneToOneRel):
699
# For OneToOneFields, don't try to order by the related object's ordering criteria.
701
elif isinstance(f.rel, models.ManyToOneRel):
702
rel_ordering = f.rel.to._meta.ordering and f.rel.to._meta.ordering[0] or f.rel.to._meta.pk.column
703
lookup_order_field = '%s.%s' % (f.rel.to._meta.db_table, rel_ordering)
706
qs = qs.order_by((self.order_type == 'desc' and '-' or '') + lookup_order_field)
708
# Apply keyword searches.
709
if self.lookup_opts.admin.search_fields and self.query:
710
for bit in self.query.split():
711
or_queries = [models.Q(**{'%s__icontains' % field_name: bit}) for field_name in self.lookup_opts.admin.search_fields]
712
other_qs = QuerySet(self.model)
713
other_qs = other_qs.filter(reduce(operator.or_, or_queries))
716
if self.opts.one_to_one_field:
717
qs = qs.filter(**self.opts.one_to_one_field.rel.limit_choices_to)
721
def url_for_result(self, result):
722
return "%s/" % quote(getattr(result, self.pk_attname))
724
def change_list(request, app_label, model_name):
725
model = models.get_model(app_label, model_name)
727
raise Http404, "App %r, model %r, not found" % (app_label, model_name)
728
if not request.user.has_perm(app_label + '.' + model._meta.get_change_permission()):
729
raise PermissionDenied
731
cl = ChangeList(request, model)
732
except IncorrectLookupParameters:
733
return HttpResponseRedirect(request.path)
734
c = template.RequestContext(request, {
736
'is_popup': cl.is_popup,
739
c.update({'has_add_permission': c['perms'][app_label][cl.opts.get_add_permission()]}),
740
return render_to_response(['admin/%s/%s/change_list.html' % (app_label, cl.opts.object_name.lower()),
741
'admin/%s/change_list.html' % app_label,
742
'admin/change_list.html'], context_instance=c)
743
change_list = staff_member_required(never_cache(change_list))