37 ALL_VAR = 'all' |
22 ALL_VAR = 'all' |
38 ORDER_VAR = 'o' |
23 ORDER_VAR = 'o' |
39 ORDER_TYPE_VAR = 'ot' |
24 ORDER_TYPE_VAR = 'ot' |
40 PAGE_VAR = 'p' |
25 PAGE_VAR = 'p' |
41 SEARCH_VAR = 'q' |
26 SEARCH_VAR = 'q' |
|
27 TO_FIELD_VAR = 't' |
42 IS_POPUP_VAR = 'pop' |
28 IS_POPUP_VAR = 'pop' |
43 ERROR_FLAG = 'e' |
29 ERROR_FLAG = 'e' |
44 |
30 |
45 # Text to display within change-list table cells if the value is blank. |
31 # Text to display within change-list table cells if the value is blank. |
46 EMPTY_CHANGELIST_VALUE = '(None)' |
32 EMPTY_CHANGELIST_VALUE = '(None)' |
47 |
33 |
48 use_raw_id_admin = lambda field: isinstance(field.rel, (models.ManyToOneRel, models.ManyToManyRel)) and field.rel.raw_id_admin |
|
49 |
|
50 class IncorrectLookupParameters(Exception): |
|
51 pass |
|
52 |
|
53 def quote(s): |
|
54 """ |
|
55 Ensure that primary key values do not confuse the admin URLs by escaping |
|
56 any '/', '_' and ':' characters. Similar to urllib.quote, except that the |
|
57 quoting is slightly different so that it doesn't get automatically |
|
58 unquoted by the Web browser. |
|
59 """ |
|
60 if type(s) != type(''): |
|
61 return s |
|
62 res = list(s) |
|
63 for i in range(len(res)): |
|
64 c = res[i] |
|
65 if c in ':/_': |
|
66 res[i] = '_%02X' % ord(c) |
|
67 return ''.join(res) |
|
68 |
|
69 def unquote(s): |
|
70 """ |
|
71 Undo the effects of quote(). Based heavily on urllib.unquote(). |
|
72 """ |
|
73 mychr = chr |
|
74 myatoi = int |
|
75 list = s.split('_') |
|
76 res = [list[0]] |
|
77 myappend = res.append |
|
78 del list[0] |
|
79 for item in list: |
|
80 if item[1:2]: |
|
81 try: |
|
82 myappend(mychr(myatoi(item[:2], 16)) + item[2:]) |
|
83 except ValueError: |
|
84 myappend('_' + item) |
|
85 else: |
|
86 myappend('_' + item) |
|
87 return "".join(res) |
|
88 |
|
89 def get_javascript_imports(opts, auto_populated_fields, field_sets): |
|
90 # Put in any necessary JavaScript imports. |
|
91 js = ['js/core.js', 'js/admin/RelatedObjectLookups.js'] |
|
92 if auto_populated_fields: |
|
93 js.append('js/urlify.js') |
|
94 if opts.has_field_type(models.DateTimeField) or opts.has_field_type(models.TimeField) or opts.has_field_type(models.DateField): |
|
95 js.extend(['js/calendar.js', 'js/admin/DateTimeShortcuts.js']) |
|
96 if opts.get_ordered_objects(): |
|
97 js.extend(['js/getElementsBySelector.js', 'js/dom-drag.js' , 'js/admin/ordering.js']) |
|
98 if opts.admin.js: |
|
99 js.extend(opts.admin.js) |
|
100 seen_collapse = False |
|
101 for field_set in field_sets: |
|
102 if not seen_collapse and 'collapse' in field_set.classes: |
|
103 seen_collapse = True |
|
104 js.append('js/admin/CollapsedFieldsets.js') |
|
105 |
|
106 for field_line in field_set: |
|
107 try: |
|
108 for f in field_line: |
|
109 if f.rel and isinstance(f, models.ManyToManyField) and f.rel.filter_interface: |
|
110 js.extend(['js/SelectBox.js' , 'js/SelectFilter2.js']) |
|
111 raise StopIteration |
|
112 except StopIteration: |
|
113 break |
|
114 return js |
|
115 |
|
116 class AdminBoundField(object): |
|
117 def __init__(self, field, field_mapping, original): |
|
118 self.field = field |
|
119 self.original = original |
|
120 self.form_fields = [field_mapping[name] for name in self.field.get_manipulator_field_names('')] |
|
121 self.element_id = self.form_fields[0].get_id() |
|
122 self.has_label_first = not isinstance(self.field, models.BooleanField) |
|
123 self.raw_id_admin = use_raw_id_admin(field) |
|
124 self.is_date_time = isinstance(field, models.DateTimeField) |
|
125 self.is_file_field = isinstance(field, models.FileField) |
|
126 self.needs_add_label = field.rel and (isinstance(field.rel, models.ManyToOneRel) or isinstance(field.rel, models.ManyToManyRel)) and field.rel.to._meta.admin |
|
127 self.hidden = isinstance(self.field, models.AutoField) |
|
128 self.first = False |
|
129 |
|
130 classes = [] |
|
131 if self.raw_id_admin: |
|
132 classes.append('nowrap') |
|
133 if max([bool(f.errors()) for f in self.form_fields]): |
|
134 classes.append('error') |
|
135 if classes: |
|
136 self.cell_class_attribute = u' class="%s" ' % ' '.join(classes) |
|
137 self._repr_filled = False |
|
138 |
|
139 if field.rel: |
|
140 self.related_url = mark_safe(u'../../../%s/%s/' |
|
141 % (field.rel.to._meta.app_label, |
|
142 field.rel.to._meta.object_name.lower())) |
|
143 |
|
144 def original_value(self): |
|
145 if self.original: |
|
146 return self.original.__dict__[self.field.attname] |
|
147 |
|
148 def existing_display(self): |
|
149 try: |
|
150 return self._display |
|
151 except AttributeError: |
|
152 if isinstance(self.field.rel, models.ManyToOneRel): |
|
153 self._display = force_unicode(getattr(self.original, self.field.name), strings_only=True) |
|
154 elif isinstance(self.field.rel, models.ManyToManyRel): |
|
155 self._display = u", ".join([force_unicode(obj) for obj in getattr(self.original, self.field.name).all()]) |
|
156 return self._display |
|
157 |
|
158 def __repr__(self): |
|
159 return repr(self.__dict__) |
|
160 |
|
161 def html_error_list(self): |
|
162 return mark_safe(" ".join([form_field.html_error_list() for form_field in self.form_fields if form_field.errors])) |
|
163 |
|
164 def original_url(self): |
|
165 if self.is_file_field and self.original and self.field.attname: |
|
166 url_method = getattr(self.original, 'get_%s_url' % self.field.attname) |
|
167 if callable(url_method): |
|
168 return url_method() |
|
169 return '' |
|
170 |
|
171 class AdminBoundFieldLine(object): |
|
172 def __init__(self, field_line, field_mapping, original): |
|
173 self.bound_fields = [field.bind(field_mapping, original, AdminBoundField) for field in field_line] |
|
174 for bound_field in self: |
|
175 bound_field.first = True |
|
176 break |
|
177 |
|
178 def __iter__(self): |
|
179 for bound_field in self.bound_fields: |
|
180 yield bound_field |
|
181 |
|
182 def __len__(self): |
|
183 return len(self.bound_fields) |
|
184 |
|
185 class AdminBoundFieldSet(object): |
|
186 def __init__(self, field_set, field_mapping, original): |
|
187 self.name = field_set.name |
|
188 self.classes = field_set.classes |
|
189 self.description = field_set.description |
|
190 self.bound_field_lines = [field_line.bind(field_mapping, original, AdminBoundFieldLine) for field_line in field_set] |
|
191 |
|
192 def __iter__(self): |
|
193 for bound_field_line in self.bound_field_lines: |
|
194 yield bound_field_line |
|
195 |
|
196 def __len__(self): |
|
197 return len(self.bound_field_lines) |
|
198 |
|
199 def render_change_form(model, manipulator, context, add=False, change=False, form_url=''): |
|
200 opts = model._meta |
|
201 app_label = opts.app_label |
|
202 auto_populated_fields = [f for f in opts.fields if f.prepopulate_from] |
|
203 field_sets = opts.admin.get_field_sets(opts) |
|
204 original = getattr(manipulator, 'original_object', None) |
|
205 bound_field_sets = [field_set.bind(context['form'], original, AdminBoundFieldSet) for field_set in field_sets] |
|
206 first_form_field_id = bound_field_sets[0].bound_field_lines[0].bound_fields[0].form_fields[0].get_id(); |
|
207 ordered_objects = opts.get_ordered_objects() |
|
208 inline_related_objects = opts.get_followed_related_objects(manipulator.follow) |
|
209 extra_context = { |
|
210 'add': add, |
|
211 'change': change, |
|
212 'has_delete_permission': context['perms'][app_label][opts.get_delete_permission()], |
|
213 'has_change_permission': context['perms'][app_label][opts.get_change_permission()], |
|
214 'has_file_field': opts.has_field_type(models.FileField), |
|
215 'has_absolute_url': hasattr(model, 'get_absolute_url'), |
|
216 'auto_populated_fields': auto_populated_fields, |
|
217 'bound_field_sets': bound_field_sets, |
|
218 'first_form_field_id': first_form_field_id, |
|
219 'javascript_imports': get_javascript_imports(opts, auto_populated_fields, field_sets), |
|
220 'ordered_objects': ordered_objects, |
|
221 'inline_related_objects': inline_related_objects, |
|
222 'form_url': mark_safe(form_url), |
|
223 'opts': opts, |
|
224 'content_type_id': ContentType.objects.get_for_model(model).id, |
|
225 } |
|
226 context.update(extra_context) |
|
227 return render_to_response([ |
|
228 "admin/%s/%s/change_form.html" % (app_label, opts.object_name.lower()), |
|
229 "admin/%s/change_form.html" % app_label, |
|
230 "admin/change_form.html"], context_instance=context) |
|
231 |
|
232 def index(request): |
|
233 return render_to_response('admin/index.html', {'title': _('Site administration')}, context_instance=template.RequestContext(request)) |
|
234 index = staff_member_required(never_cache(index)) |
|
235 |
|
236 def add_stage(request, app_label, model_name, show_delete=False, form_url='', post_url=None, post_url_continue='../%s/', object_id_override=None): |
|
237 model = models.get_model(app_label, model_name) |
|
238 if model is None: |
|
239 raise Http404("App %r, model %r, not found" % (app_label, model_name)) |
|
240 opts = model._meta |
|
241 |
|
242 if not request.user.has_perm(app_label + '.' + opts.get_add_permission()): |
|
243 raise PermissionDenied |
|
244 |
|
245 if post_url is None: |
|
246 if request.user.has_perm(app_label + '.' + opts.get_change_permission()): |
|
247 # redirect to list view |
|
248 post_url = '../' |
|
249 else: |
|
250 # Object list will give 'Permission Denied', so go back to admin home |
|
251 post_url = '../../../' |
|
252 |
|
253 manipulator = model.AddManipulator() |
|
254 if request.POST: |
|
255 new_data = request.POST.copy() |
|
256 |
|
257 if opts.has_field_type(models.FileField): |
|
258 new_data.update(request.FILES) |
|
259 |
|
260 errors = manipulator.get_validation_errors(new_data) |
|
261 manipulator.do_html2python(new_data) |
|
262 |
|
263 if not errors: |
|
264 new_object = manipulator.save(new_data) |
|
265 pk_value = new_object._get_pk_val() |
|
266 LogEntry.objects.log_action(request.user.id, ContentType.objects.get_for_model(model).id, pk_value, force_unicode(new_object), ADDITION) |
|
267 msg = _('The %(name)s "%(obj)s" was added successfully.') % {'name': force_unicode(opts.verbose_name), 'obj': force_unicode(new_object)} |
|
268 # Here, we distinguish between different save types by checking for |
|
269 # the presence of keys in request.POST. |
|
270 if "_continue" in request.POST: |
|
271 request.user.message_set.create(message=msg + ' ' + _("You may edit it again below.")) |
|
272 if "_popup" in request.POST: |
|
273 post_url_continue += "?_popup=1" |
|
274 return HttpResponseRedirect(post_url_continue % pk_value) |
|
275 if "_popup" in request.POST: |
|
276 return HttpResponse('<script type="text/javascript">opener.dismissAddAnotherPopup(window, "%s", "%s");</script>' % \ |
|
277 # escape() calls force_unicode. |
|
278 (escape(pk_value), escape(new_object))) |
|
279 elif "_addanother" in request.POST: |
|
280 request.user.message_set.create(message=msg + ' ' + (_("You may add another %s below.") % force_unicode(opts.verbose_name))) |
|
281 return HttpResponseRedirect(request.path) |
|
282 else: |
|
283 request.user.message_set.create(message=msg) |
|
284 return HttpResponseRedirect(post_url) |
|
285 else: |
|
286 # Add default data. |
|
287 new_data = manipulator.flatten_data() |
|
288 |
|
289 # Override the defaults with GET params, if they exist. |
|
290 new_data.update(dict(request.GET.items())) |
|
291 |
|
292 errors = {} |
|
293 |
|
294 # Populate the FormWrapper. |
|
295 form = oldforms.FormWrapper(manipulator, new_data, errors) |
|
296 |
|
297 c = template.RequestContext(request, { |
|
298 'title': _('Add %s') % force_unicode(opts.verbose_name), |
|
299 'form': form, |
|
300 'is_popup': '_popup' in request.REQUEST, |
|
301 'show_delete': show_delete, |
|
302 }) |
|
303 |
|
304 if object_id_override is not None: |
|
305 c['object_id'] = object_id_override |
|
306 |
|
307 return render_change_form(model, manipulator, c, add=True) |
|
308 add_stage = staff_member_required(never_cache(add_stage)) |
|
309 |
|
310 def change_stage(request, app_label, model_name, object_id): |
|
311 model = models.get_model(app_label, model_name) |
|
312 object_id = unquote(object_id) |
|
313 if model is None: |
|
314 raise Http404("App %r, model %r, not found" % (app_label, model_name)) |
|
315 opts = model._meta |
|
316 |
|
317 if not request.user.has_perm(app_label + '.' + opts.get_change_permission()): |
|
318 raise PermissionDenied |
|
319 |
|
320 if request.POST and "_saveasnew" in request.POST: |
|
321 return add_stage(request, app_label, model_name, form_url='../../add/') |
|
322 |
|
323 try: |
|
324 manipulator = model.ChangeManipulator(object_id) |
|
325 except model.DoesNotExist: |
|
326 raise Http404('%s object with primary key %r does not exist' % (model_name, escape(object_id))) |
|
327 |
|
328 if request.POST: |
|
329 new_data = request.POST.copy() |
|
330 |
|
331 if opts.has_field_type(models.FileField): |
|
332 new_data.update(request.FILES) |
|
333 |
|
334 errors = manipulator.get_validation_errors(new_data) |
|
335 manipulator.do_html2python(new_data) |
|
336 |
|
337 if not errors: |
|
338 new_object = manipulator.save(new_data) |
|
339 pk_value = new_object._get_pk_val() |
|
340 |
|
341 # Construct the change message. |
|
342 change_message = [] |
|
343 if manipulator.fields_added: |
|
344 change_message.append(_('Added %s.') % get_text_list(manipulator.fields_added, _('and'))) |
|
345 if manipulator.fields_changed: |
|
346 change_message.append(_('Changed %s.') % get_text_list(manipulator.fields_changed, _('and'))) |
|
347 if manipulator.fields_deleted: |
|
348 change_message.append(_('Deleted %s.') % get_text_list(manipulator.fields_deleted, _('and'))) |
|
349 change_message = ' '.join(change_message) |
|
350 if not change_message: |
|
351 change_message = _('No fields changed.') |
|
352 LogEntry.objects.log_action(request.user.id, ContentType.objects.get_for_model(model).id, pk_value, force_unicode(new_object), CHANGE, change_message) |
|
353 |
|
354 msg = _('The %(name)s "%(obj)s" was changed successfully.') % {'name': force_unicode(opts.verbose_name), 'obj': force_unicode(new_object)} |
|
355 if "_continue" in request.POST: |
|
356 request.user.message_set.create(message=msg + ' ' + _("You may edit it again below.")) |
|
357 if '_popup' in request.REQUEST: |
|
358 return HttpResponseRedirect(request.path + "?_popup=1") |
|
359 else: |
|
360 return HttpResponseRedirect(request.path) |
|
361 elif "_saveasnew" in request.POST: |
|
362 request.user.message_set.create(message=_('The %(name)s "%(obj)s" was added successfully. You may edit it again below.') % {'name': force_unicode(opts.verbose_name), 'obj': force_unicode(new_object)}) |
|
363 return HttpResponseRedirect("../%s/" % pk_value) |
|
364 elif "_addanother" in request.POST: |
|
365 request.user.message_set.create(message=msg + ' ' + (_("You may add another %s below.") % force_unicode(opts.verbose_name))) |
|
366 return HttpResponseRedirect("../add/") |
|
367 else: |
|
368 request.user.message_set.create(message=msg) |
|
369 return HttpResponseRedirect("../") |
|
370 else: |
|
371 # Populate new_data with a "flattened" version of the current data. |
|
372 new_data = manipulator.flatten_data() |
|
373 |
|
374 # TODO: do this in flatten_data... |
|
375 # If the object has ordered objects on its admin page, get the existing |
|
376 # order and flatten it into a comma-separated list of IDs. |
|
377 |
|
378 id_order_list = [] |
|
379 for rel_obj in opts.get_ordered_objects(): |
|
380 id_order_list.extend(getattr(manipulator.original_object, 'get_%s_order' % rel_obj.object_name.lower())()) |
|
381 if id_order_list: |
|
382 new_data['order_'] = ','.join(map(str, id_order_list)) |
|
383 errors = {} |
|
384 |
|
385 # Populate the FormWrapper. |
|
386 form = oldforms.FormWrapper(manipulator, new_data, errors) |
|
387 form.original = manipulator.original_object |
|
388 form.order_objects = [] |
|
389 |
|
390 #TODO Should be done in flatten_data / FormWrapper construction |
|
391 for related in opts.get_followed_related_objects(): |
|
392 wrt = related.opts.order_with_respect_to |
|
393 if wrt and wrt.rel and wrt.rel.to == opts: |
|
394 func = getattr(manipulator.original_object, 'get_%s_list' % |
|
395 related.get_accessor_name()) |
|
396 orig_list = func() |
|
397 form.order_objects.extend(orig_list) |
|
398 |
|
399 c = template.RequestContext(request, { |
|
400 'title': _('Change %s') % force_unicode(opts.verbose_name), |
|
401 'form': form, |
|
402 'object_id': object_id, |
|
403 'original': manipulator.original_object, |
|
404 'is_popup': '_popup' in request.REQUEST, |
|
405 }) |
|
406 return render_change_form(model, manipulator, c, change=True) |
|
407 change_stage = staff_member_required(never_cache(change_stage)) |
|
408 |
|
409 def _nest_help(obj, depth, val): |
|
410 current = obj |
|
411 for i in range(depth): |
|
412 current = current[-1] |
|
413 current.append(val) |
|
414 |
|
415 def _get_deleted_objects(deleted_objects, perms_needed, user, obj, opts, current_depth): |
|
416 "Helper function that recursively populates deleted_objects." |
|
417 nh = _nest_help # Bind to local variable for performance |
|
418 if current_depth > 16: |
|
419 return # Avoid recursing too deep. |
|
420 opts_seen = [] |
|
421 for related in opts.get_all_related_objects(): |
|
422 if related.opts in opts_seen: |
|
423 continue |
|
424 opts_seen.append(related.opts) |
|
425 rel_opts_name = related.get_accessor_name() |
|
426 if isinstance(related.field.rel, models.OneToOneRel): |
|
427 try: |
|
428 sub_obj = getattr(obj, rel_opts_name) |
|
429 except ObjectDoesNotExist: |
|
430 pass |
|
431 else: |
|
432 if related.opts.admin: |
|
433 p = '%s.%s' % (related.opts.app_label, related.opts.get_delete_permission()) |
|
434 if not user.has_perm(p): |
|
435 perms_needed.add(related.opts.verbose_name) |
|
436 # We don't care about populating deleted_objects now. |
|
437 continue |
|
438 if related.field.rel.edit_inline or not related.opts.admin: |
|
439 # Don't display link to edit, because it either has no |
|
440 # admin or is edited inline. |
|
441 nh(deleted_objects, current_depth, [mark_safe(u'%s: %s' % (force_unicode(capfirst(related.opts.verbose_name)), sub_obj)), []]) |
|
442 else: |
|
443 # Display a link to the admin page. |
|
444 nh(deleted_objects, current_depth, [mark_safe(u'%s: <a href="../../../../%s/%s/%s/">%s</a>' % |
|
445 (escape(force_unicode(capfirst(related.opts.verbose_name))), |
|
446 related.opts.app_label, |
|
447 related.opts.object_name.lower(), |
|
448 sub_obj._get_pk_val(), sub_obj)), []]) |
|
449 _get_deleted_objects(deleted_objects, perms_needed, user, sub_obj, related.opts, current_depth+2) |
|
450 else: |
|
451 has_related_objs = False |
|
452 for sub_obj in getattr(obj, rel_opts_name).all(): |
|
453 has_related_objs = True |
|
454 if related.field.rel.edit_inline or not related.opts.admin: |
|
455 # Don't display link to edit, because it either has no |
|
456 # admin or is edited inline. |
|
457 nh(deleted_objects, current_depth, [u'%s: %s' % (force_unicode(capfirst(related.opts.verbose_name)), escape(sub_obj)), []]) |
|
458 else: |
|
459 # Display a link to the admin page. |
|
460 nh(deleted_objects, current_depth, [mark_safe(u'%s: <a href="../../../../%s/%s/%s/">%s</a>' % \ |
|
461 (escape(force_unicode(capfirst(related.opts.verbose_name))), related.opts.app_label, related.opts.object_name.lower(), sub_obj._get_pk_val(), escape(sub_obj))), []]) |
|
462 _get_deleted_objects(deleted_objects, perms_needed, user, sub_obj, related.opts, current_depth+2) |
|
463 # If there were related objects, and the user doesn't have |
|
464 # permission to delete them, add the missing perm to perms_needed. |
|
465 if related.opts.admin and has_related_objs: |
|
466 p = '%s.%s' % (related.opts.app_label, related.opts.get_delete_permission()) |
|
467 if not user.has_perm(p): |
|
468 perms_needed.add(related.opts.verbose_name) |
|
469 for related in opts.get_all_related_many_to_many_objects(): |
|
470 if related.opts in opts_seen: |
|
471 continue |
|
472 opts_seen.append(related.opts) |
|
473 rel_opts_name = related.get_accessor_name() |
|
474 has_related_objs = False |
|
475 |
|
476 # related.get_accessor_name() could return None for symmetrical relationships |
|
477 if rel_opts_name: |
|
478 rel_objs = getattr(obj, rel_opts_name, None) |
|
479 if rel_objs: |
|
480 has_related_objs = True |
|
481 |
|
482 if has_related_objs: |
|
483 for sub_obj in rel_objs.all(): |
|
484 if related.field.rel.edit_inline or not related.opts.admin: |
|
485 # Don't display link to edit, because it either has no |
|
486 # admin or is edited inline. |
|
487 nh(deleted_objects, current_depth, [_('One or more %(fieldname)s in %(name)s: %(obj)s') % \ |
|
488 {'fieldname': force_unicode(related.field.verbose_name), 'name': force_unicode(related.opts.verbose_name), 'obj': escape(sub_obj)}, []]) |
|
489 else: |
|
490 # Display a link to the admin page. |
|
491 nh(deleted_objects, current_depth, [ |
|
492 mark_safe((_('One or more %(fieldname)s in %(name)s:') % {'fieldname': escape(force_unicode(related.field.verbose_name)), 'name': escape(force_unicode(related.opts.verbose_name))}) + \ |
|
493 (u' <a href="../../../../%s/%s/%s/">%s</a>' % \ |
|
494 (related.opts.app_label, related.opts.module_name, sub_obj._get_pk_val(), escape(sub_obj)))), []]) |
|
495 # If there were related objects, and the user doesn't have |
|
496 # permission to change them, add the missing perm to perms_needed. |
|
497 if related.opts.admin and has_related_objs: |
|
498 p = u'%s.%s' % (related.opts.app_label, related.opts.get_change_permission()) |
|
499 if not user.has_perm(p): |
|
500 perms_needed.add(related.opts.verbose_name) |
|
501 |
|
502 def delete_stage(request, app_label, model_name, object_id): |
|
503 model = models.get_model(app_label, model_name) |
|
504 object_id = unquote(object_id) |
|
505 if model is None: |
|
506 raise Http404("App %r, model %r, not found" % (app_label, model_name)) |
|
507 opts = model._meta |
|
508 if not request.user.has_perm(app_label + '.' + opts.get_delete_permission()): |
|
509 raise PermissionDenied |
|
510 obj = get_object_or_404(model, pk=object_id) |
|
511 |
|
512 # Populate deleted_objects, a data structure of all related objects that |
|
513 # will also be deleted. |
|
514 deleted_objects = [mark_safe(u'%s: <a href="../../%s/">%s</a>' % (escape(force_unicode(capfirst(opts.verbose_name))), force_unicode(object_id), escape(obj))), []] |
|
515 perms_needed = set() |
|
516 _get_deleted_objects(deleted_objects, perms_needed, request.user, obj, opts, 1) |
|
517 |
|
518 if request.POST: # The user has already confirmed the deletion. |
|
519 if perms_needed: |
|
520 raise PermissionDenied |
|
521 obj_display = force_unicode(obj) |
|
522 obj.delete() |
|
523 LogEntry.objects.log_action(request.user.id, ContentType.objects.get_for_model(model).id, object_id, obj_display, DELETION) |
|
524 request.user.message_set.create(message=_('The %(name)s "%(obj)s" was deleted successfully.') % {'name': force_unicode(opts.verbose_name), 'obj': obj_display}) |
|
525 return HttpResponseRedirect("../../") |
|
526 extra_context = { |
|
527 "title": _("Are you sure?"), |
|
528 "object_name": force_unicode(opts.verbose_name), |
|
529 "object": obj, |
|
530 "deleted_objects": deleted_objects, |
|
531 "perms_lacking": perms_needed, |
|
532 "opts": model._meta, |
|
533 } |
|
534 return render_to_response(["admin/%s/%s/delete_confirmation.html" % (app_label, opts.object_name.lower() ), |
|
535 "admin/%s/delete_confirmation.html" % app_label , |
|
536 "admin/delete_confirmation.html"], extra_context, context_instance=template.RequestContext(request)) |
|
537 delete_stage = staff_member_required(never_cache(delete_stage)) |
|
538 |
|
539 def history(request, app_label, model_name, object_id): |
|
540 model = models.get_model(app_label, model_name) |
|
541 object_id = unquote(object_id) |
|
542 if model is None: |
|
543 raise Http404("App %r, model %r, not found" % (app_label, model_name)) |
|
544 action_list = LogEntry.objects.filter(object_id=object_id, |
|
545 content_type__id__exact=ContentType.objects.get_for_model(model).id).select_related().order_by('action_time') |
|
546 # If no history was found, see whether this object even exists. |
|
547 obj = get_object_or_404(model, pk=object_id) |
|
548 extra_context = { |
|
549 'title': _('Change history: %s') % obj, |
|
550 'action_list': action_list, |
|
551 'module_name': force_unicode(capfirst(model._meta.verbose_name_plural)), |
|
552 'object': obj, |
|
553 } |
|
554 return render_to_response(["admin/%s/%s/object_history.html" % (app_label, model._meta.object_name.lower()), |
|
555 "admin/%s/object_history.html" % app_label , |
|
556 "admin/object_history.html"], extra_context, context_instance=template.RequestContext(request)) |
|
557 history = staff_member_required(never_cache(history)) |
|
558 |
|
559 class ChangeList(object): |
34 class ChangeList(object): |
560 def __init__(self, request, model): |
35 def __init__(self, request, model, list_display, list_display_links, list_filter, date_hierarchy, search_fields, list_select_related, list_per_page, model_admin): |
561 self.model = model |
36 self.model = model |
562 self.opts = model._meta |
37 self.opts = model._meta |
563 self.lookup_opts = self.opts |
38 self.lookup_opts = self.opts |
564 self.manager = self.opts.admin.manager |
39 self.root_query_set = model_admin.queryset(request) |
|
40 self.list_display = list_display |
|
41 self.list_display_links = list_display_links |
|
42 self.list_filter = list_filter |
|
43 self.date_hierarchy = date_hierarchy |
|
44 self.search_fields = search_fields |
|
45 self.list_select_related = list_select_related |
|
46 self.list_per_page = list_per_page |
|
47 self.model_admin = model_admin |
565 |
48 |
566 # Get search parameters from the query string. |
49 # Get search parameters from the query string. |
567 try: |
50 try: |
568 self.page_num = int(request.GET.get(PAGE_VAR, 0)) |
51 self.page_num = int(request.GET.get(PAGE_VAR, 0)) |
569 except ValueError: |
52 except ValueError: |
570 self.page_num = 0 |
53 self.page_num = 0 |
571 self.show_all = ALL_VAR in request.GET |
54 self.show_all = ALL_VAR in request.GET |
572 self.is_popup = IS_POPUP_VAR in request.GET |
55 self.is_popup = IS_POPUP_VAR in request.GET |
|
56 self.to_field = request.GET.get(TO_FIELD_VAR) |
573 self.params = dict(request.GET.items()) |
57 self.params = dict(request.GET.items()) |
574 if PAGE_VAR in self.params: |
58 if PAGE_VAR in self.params: |
575 del self.params[PAGE_VAR] |
59 del self.params[PAGE_VAR] |
|
60 if TO_FIELD_VAR in self.params: |
|
61 del self.params[TO_FIELD_VAR] |
576 if ERROR_FLAG in self.params: |
62 if ERROR_FLAG in self.params: |
577 del self.params[ERROR_FLAG] |
63 del self.params[ERROR_FLAG] |
578 |
64 |
579 self.order_field, self.order_type = self.get_ordering() |
65 self.order_field, self.order_type = self.get_ordering() |
580 self.query = request.GET.get(SEARCH_VAR, '') |
66 self.query = request.GET.get(SEARCH_VAR, '') |
581 self.query_set = self.get_query_set() |
67 self.query_set = self.get_query_set() |
582 self.get_results(request) |
68 self.get_results(request) |
583 self.title = (self.is_popup and _('Select %s') % force_unicode(self.opts.verbose_name) or _('Select %s to change') % force_unicode(self.opts.verbose_name)) |
69 self.title = (self.is_popup and ugettext('Select %s') % force_unicode(self.opts.verbose_name) or ugettext('Select %s to change') % force_unicode(self.opts.verbose_name)) |
584 self.filter_specs, self.has_filters = self.get_filters(request) |
70 self.filter_specs, self.has_filters = self.get_filters(request) |
585 self.pk_attname = self.lookup_opts.pk.attname |
71 self.pk_attname = self.lookup_opts.pk.attname |
586 |
72 |
587 def get_filters(self, request): |
73 def get_filters(self, request): |
588 filter_specs = [] |
74 filter_specs = [] |
589 if self.lookup_opts.admin.list_filter and not self.opts.one_to_one_field: |
75 if self.list_filter: |
590 filter_fields = [self.lookup_opts.get_field(field_name) \ |
76 filter_fields = [self.lookup_opts.get_field(field_name) for field_name in self.list_filter] |
591 for field_name in self.lookup_opts.admin.list_filter] |
|
592 for f in filter_fields: |
77 for f in filter_fields: |
593 spec = FilterSpec.create(f, request, self.params, self.model) |
78 spec = FilterSpec.create(f, request, self.params, self.model, self.model_admin) |
594 if spec and spec.has_output(): |
79 if spec and spec.has_output(): |
595 filter_specs.append(spec) |
80 filter_specs.append(spec) |
596 return filter_specs, bool(filter_specs) |
81 return filter_specs, bool(filter_specs) |
597 |
82 |
598 def get_query_string(self, new_params=None, remove=None): |
83 def get_query_string(self, new_params=None, remove=None): |