summaryrefslogtreecommitdiff
path: root/django/contrib/admin/views/main.py
diff options
context:
space:
mode:
Diffstat (limited to 'django/contrib/admin/views/main.py')
-rw-r--r--django/contrib/admin/views/main.py622
1 files changed, 40 insertions, 582 deletions
diff --git a/django/contrib/admin/views/main.py b/django/contrib/admin/views/main.py
index f1f620ed9e..926270cc68 100644
--- a/django/contrib/admin/views/main.py
+++ b/django/contrib/admin/views/main.py
@@ -1,20 +1,13 @@
-from django import oldforms, template
-from django.conf import settings
from django.contrib.admin.filterspecs import FilterSpec
-from django.contrib.admin.views.decorators import staff_member_required
-from django.views.decorators.cache import never_cache
-from django.contrib.contenttypes.models import ContentType
-from django.core.exceptions import ImproperlyConfigured, ObjectDoesNotExist, PermissionDenied
+from django.contrib.admin.options import IncorrectLookupParameters
+from django.contrib.admin.util import quote
from django.core.paginator import Paginator, InvalidPage
-from django.shortcuts import get_object_or_404, render_to_response
from django.db import models
from django.db.models.query import QuerySet
-from django.http import Http404, HttpResponse, HttpResponseRedirect
-from django.utils.html import escape
-from django.utils.text import capfirst, get_text_list
from django.utils.encoding import force_unicode, smart_str
-from django.utils.translation import ugettext as _
+from django.utils.translation import ugettext
from django.utils.safestring import mark_safe
+from django.utils.http import urlencode
import operator
try:
@@ -22,13 +15,6 @@ try:
except NameError:
from sets import Set as set # Python 2.3 fallback
-from django.contrib.admin.models import LogEntry, ADDITION, CHANGE, DELETION
-if not LogEntry._meta.installed:
- raise ImproperlyConfigured, "You'll need to put 'django.contrib.admin' in your INSTALLED_APPS setting before you can use the admin application."
-
-if 'django.core.context_processors.auth' not in settings.TEMPLATE_CONTEXT_PROCESSORS:
- 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."
-
# The system will display a "Show all" link on the change list only if the
# total result count is less than or equal to this setting.
MAX_SHOW_ALL_ALLOWED = 200
@@ -45,523 +31,20 @@ ERROR_FLAG = 'e'
# Text to display within change-list table cells if the value is blank.
EMPTY_CHANGELIST_VALUE = '(None)'
-use_raw_id_admin = lambda field: isinstance(field.rel, (models.ManyToOneRel, models.ManyToManyRel)) and field.rel.raw_id_admin
-
-class IncorrectLookupParameters(Exception):
- pass
-
-def quote(s):
- """
- Ensure that primary key values do not confuse the admin URLs by escaping
- any '/', '_' and ':' characters. Similar to urllib.quote, except that the
- quoting is slightly different so that it doesn't get automatically
- unquoted by the Web browser.
- """
- if type(s) != type(''):
- return s
- res = list(s)
- for i in range(len(res)):
- c = res[i]
- if c in ':/_':
- res[i] = '_%02X' % ord(c)
- return ''.join(res)
-
-def unquote(s):
- """
- Undo the effects of quote(). Based heavily on urllib.unquote().
- """
- mychr = chr
- myatoi = int
- list = s.split('_')
- res = [list[0]]
- myappend = res.append
- del list[0]
- for item in list:
- if item[1:2]:
- try:
- myappend(mychr(myatoi(item[:2], 16)) + item[2:])
- except ValueError:
- myappend('_' + item)
- else:
- myappend('_' + item)
- return "".join(res)
-
-def get_javascript_imports(opts, auto_populated_fields, field_sets):
-# Put in any necessary JavaScript imports.
- js = ['js/core.js', 'js/admin/RelatedObjectLookups.js']
- if auto_populated_fields:
- js.append('js/urlify.js')
- if opts.has_field_type(models.DateTimeField) or opts.has_field_type(models.TimeField) or opts.has_field_type(models.DateField):
- js.extend(['js/calendar.js', 'js/admin/DateTimeShortcuts.js'])
- if opts.get_ordered_objects():
- js.extend(['js/getElementsBySelector.js', 'js/dom-drag.js' , 'js/admin/ordering.js'])
- if opts.admin.js:
- js.extend(opts.admin.js)
- seen_collapse = False
- for field_set in field_sets:
- if not seen_collapse and 'collapse' in field_set.classes:
- seen_collapse = True
- js.append('js/admin/CollapsedFieldsets.js')
-
- for field_line in field_set:
- try:
- for f in field_line:
- if f.rel and isinstance(f, models.ManyToManyField) and f.rel.filter_interface:
- js.extend(['js/SelectBox.js' , 'js/SelectFilter2.js'])
- raise StopIteration
- except StopIteration:
- break
- return js
-
-class AdminBoundField(object):
- def __init__(self, field, field_mapping, original):
- self.field = field
- self.original = original
- self.form_fields = [field_mapping[name] for name in self.field.get_manipulator_field_names('')]
- self.element_id = self.form_fields[0].get_id()
- self.has_label_first = not isinstance(self.field, models.BooleanField)
- self.raw_id_admin = use_raw_id_admin(field)
- self.is_date_time = isinstance(field, models.DateTimeField)
- self.is_file_field = isinstance(field, models.FileField)
- self.needs_add_label = field.rel and (isinstance(field.rel, models.ManyToOneRel) or isinstance(field.rel, models.ManyToManyRel)) and field.rel.to._meta.admin
- self.hidden = isinstance(self.field, models.AutoField)
- self.first = False
-
- classes = []
- if self.raw_id_admin:
- classes.append('nowrap')
- if max([bool(f.errors()) for f in self.form_fields]):
- classes.append('error')
- if classes:
- self.cell_class_attribute = u' class="%s" ' % ' '.join(classes)
- self._repr_filled = False
-
- if field.rel:
- self.related_url = mark_safe(u'../../../%s/%s/'
- % (field.rel.to._meta.app_label,
- field.rel.to._meta.object_name.lower()))
-
- def original_value(self):
- if self.original:
- return self.original.__dict__[self.field.attname]
-
- def existing_display(self):
- try:
- return self._display
- except AttributeError:
- if isinstance(self.field.rel, models.ManyToOneRel):
- self._display = force_unicode(getattr(self.original, self.field.name), strings_only=True)
- elif isinstance(self.field.rel, models.ManyToManyRel):
- self._display = u", ".join([force_unicode(obj) for obj in getattr(self.original, self.field.name).all()])
- return self._display
-
- def __repr__(self):
- return repr(self.__dict__)
-
- def html_error_list(self):
- return mark_safe(" ".join([form_field.html_error_list() for form_field in self.form_fields if form_field.errors]))
-
- def original_url(self):
- if self.is_file_field and self.original and self.field.attname:
- url_method = getattr(self.original, 'get_%s_url' % self.field.attname)
- if callable(url_method):
- return url_method()
- return ''
-
-class AdminBoundFieldLine(object):
- def __init__(self, field_line, field_mapping, original):
- self.bound_fields = [field.bind(field_mapping, original, AdminBoundField) for field in field_line]
- for bound_field in self:
- bound_field.first = True
- break
-
- def __iter__(self):
- for bound_field in self.bound_fields:
- yield bound_field
-
- def __len__(self):
- return len(self.bound_fields)
-
-class AdminBoundFieldSet(object):
- def __init__(self, field_set, field_mapping, original):
- self.name = field_set.name
- self.classes = field_set.classes
- self.description = field_set.description
- self.bound_field_lines = [field_line.bind(field_mapping, original, AdminBoundFieldLine) for field_line in field_set]
-
- def __iter__(self):
- for bound_field_line in self.bound_field_lines:
- yield bound_field_line
-
- def __len__(self):
- return len(self.bound_field_lines)
-
-def render_change_form(model, manipulator, context, add=False, change=False, form_url=''):
- opts = model._meta
- app_label = opts.app_label
- auto_populated_fields = [f for f in opts.fields if f.prepopulate_from]
- field_sets = opts.admin.get_field_sets(opts)
- original = getattr(manipulator, 'original_object', None)
- bound_field_sets = [field_set.bind(context['form'], original, AdminBoundFieldSet) for field_set in field_sets]
- first_form_field_id = bound_field_sets[0].bound_field_lines[0].bound_fields[0].form_fields[0].get_id();
- ordered_objects = opts.get_ordered_objects()
- inline_related_objects = opts.get_followed_related_objects(manipulator.follow)
- extra_context = {
- 'add': add,
- 'change': change,
- 'has_delete_permission': context['perms'][app_label][opts.get_delete_permission()],
- 'has_change_permission': context['perms'][app_label][opts.get_change_permission()],
- 'has_file_field': opts.has_field_type(models.FileField),
- 'has_absolute_url': hasattr(model, 'get_absolute_url'),
- 'auto_populated_fields': auto_populated_fields,
- 'bound_field_sets': bound_field_sets,
- 'first_form_field_id': first_form_field_id,
- 'javascript_imports': get_javascript_imports(opts, auto_populated_fields, field_sets),
- 'ordered_objects': ordered_objects,
- 'inline_related_objects': inline_related_objects,
- 'form_url': mark_safe(form_url),
- 'opts': opts,
- 'content_type_id': ContentType.objects.get_for_model(model).id,
- }
- context.update(extra_context)
- return render_to_response([
- "admin/%s/%s/change_form.html" % (app_label, opts.object_name.lower()),
- "admin/%s/change_form.html" % app_label,
- "admin/change_form.html"], context_instance=context)
-
-def index(request):
- return render_to_response('admin/index.html', {'title': _('Site administration')}, context_instance=template.RequestContext(request))
-index = staff_member_required(never_cache(index))
-
-def add_stage(request, app_label, model_name, show_delete=False, form_url='', post_url=None, post_url_continue='../%s/', object_id_override=None):
- model = models.get_model(app_label, model_name)
- if model is None:
- raise Http404("App %r, model %r, not found" % (app_label, model_name))
- opts = model._meta
-
- if not request.user.has_perm(app_label + '.' + opts.get_add_permission()):
- raise PermissionDenied
-
- if post_url is None:
- if request.user.has_perm(app_label + '.' + opts.get_change_permission()):
- # redirect to list view
- post_url = '../'
- else:
- # Object list will give 'Permission Denied', so go back to admin home
- post_url = '../../../'
-
- manipulator = model.AddManipulator()
- if request.POST:
- new_data = request.POST.copy()
-
- if opts.has_field_type(models.FileField):
- new_data.update(request.FILES)
-
- errors = manipulator.get_validation_errors(new_data)
- manipulator.do_html2python(new_data)
-
- if not errors:
- new_object = manipulator.save(new_data)
- pk_value = new_object._get_pk_val()
- LogEntry.objects.log_action(request.user.id, ContentType.objects.get_for_model(model).id, pk_value, force_unicode(new_object), ADDITION)
- msg = _('The %(name)s "%(obj)s" was added successfully.') % {'name': force_unicode(opts.verbose_name), 'obj': force_unicode(new_object)}
- # Here, we distinguish between different save types by checking for
- # the presence of keys in request.POST.
- if "_continue" in request.POST:
- request.user.message_set.create(message=msg + ' ' + _("You may edit it again below."))
- if "_popup" in request.POST:
- post_url_continue += "?_popup=1"
- return HttpResponseRedirect(post_url_continue % pk_value)
- if "_popup" in request.POST:
- return HttpResponse('<script type="text/javascript">opener.dismissAddAnotherPopup(window, "%s", "%s");</script>' % \
- # escape() calls force_unicode.
- (escape(pk_value), escape(new_object)))
- elif "_addanother" in request.POST:
- request.user.message_set.create(message=msg + ' ' + (_("You may add another %s below.") % force_unicode(opts.verbose_name)))
- return HttpResponseRedirect(request.path)
- else:
- request.user.message_set.create(message=msg)
- return HttpResponseRedirect(post_url)
- else:
- # Add default data.
- new_data = manipulator.flatten_data()
-
- # Override the defaults with GET params, if they exist.
- new_data.update(dict(request.GET.items()))
-
- errors = {}
-
- # Populate the FormWrapper.
- form = oldforms.FormWrapper(manipulator, new_data, errors)
-
- c = template.RequestContext(request, {
- 'title': _('Add %s') % force_unicode(opts.verbose_name),
- 'form': form,
- 'is_popup': '_popup' in request.REQUEST,
- 'show_delete': show_delete,
- })
-
- if object_id_override is not None:
- c['object_id'] = object_id_override
-
- return render_change_form(model, manipulator, c, add=True)
-add_stage = staff_member_required(never_cache(add_stage))
-
-def change_stage(request, app_label, model_name, object_id):
- model = models.get_model(app_label, model_name)
- object_id = unquote(object_id)
- if model is None:
- raise Http404("App %r, model %r, not found" % (app_label, model_name))
- opts = model._meta
-
- if not request.user.has_perm(app_label + '.' + opts.get_change_permission()):
- raise PermissionDenied
-
- if request.POST and "_saveasnew" in request.POST:
- return add_stage(request, app_label, model_name, form_url='../../add/')
-
- try:
- manipulator = model.ChangeManipulator(object_id)
- except model.DoesNotExist:
- raise Http404('%s object with primary key %r does not exist' % (model_name, escape(object_id)))
-
- if request.POST:
- new_data = request.POST.copy()
-
- if opts.has_field_type(models.FileField):
- new_data.update(request.FILES)
-
- errors = manipulator.get_validation_errors(new_data)
- manipulator.do_html2python(new_data)
-
- if not errors:
- new_object = manipulator.save(new_data)
- pk_value = new_object._get_pk_val()
-
- # Construct the change message.
- change_message = []
- if manipulator.fields_added:
- change_message.append(_('Added %s.') % get_text_list(manipulator.fields_added, _('and')))
- if manipulator.fields_changed:
- change_message.append(_('Changed %s.') % get_text_list(manipulator.fields_changed, _('and')))
- if manipulator.fields_deleted:
- change_message.append(_('Deleted %s.') % get_text_list(manipulator.fields_deleted, _('and')))
- change_message = ' '.join(change_message)
- if not change_message:
- change_message = _('No fields changed.')
- LogEntry.objects.log_action(request.user.id, ContentType.objects.get_for_model(model).id, pk_value, force_unicode(new_object), CHANGE, change_message)
-
- msg = _('The %(name)s "%(obj)s" was changed successfully.') % {'name': force_unicode(opts.verbose_name), 'obj': force_unicode(new_object)}
- if "_continue" in request.POST:
- request.user.message_set.create(message=msg + ' ' + _("You may edit it again below."))
- if '_popup' in request.REQUEST:
- return HttpResponseRedirect(request.path + "?_popup=1")
- else:
- return HttpResponseRedirect(request.path)
- elif "_saveasnew" in request.POST:
- 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)})
- return HttpResponseRedirect("../%s/" % pk_value)
- elif "_addanother" in request.POST:
- request.user.message_set.create(message=msg + ' ' + (_("You may add another %s below.") % force_unicode(opts.verbose_name)))
- return HttpResponseRedirect("../add/")
- else:
- request.user.message_set.create(message=msg)
- return HttpResponseRedirect("../")
- else:
- # Populate new_data with a "flattened" version of the current data.
- new_data = manipulator.flatten_data()
-
- # TODO: do this in flatten_data...
- # If the object has ordered objects on its admin page, get the existing
- # order and flatten it into a comma-separated list of IDs.
-
- id_order_list = []
- for rel_obj in opts.get_ordered_objects():
- id_order_list.extend(getattr(manipulator.original_object, 'get_%s_order' % rel_obj.object_name.lower())())
- if id_order_list:
- new_data['order_'] = ','.join(map(str, id_order_list))
- errors = {}
-
- # Populate the FormWrapper.
- form = oldforms.FormWrapper(manipulator, new_data, errors)
- form.original = manipulator.original_object
- form.order_objects = []
-
- #TODO Should be done in flatten_data / FormWrapper construction
- for related in opts.get_followed_related_objects():
- wrt = related.opts.order_with_respect_to
- if wrt and wrt.rel and wrt.rel.to == opts:
- func = getattr(manipulator.original_object, 'get_%s_list' %
- related.get_accessor_name())
- orig_list = func()
- form.order_objects.extend(orig_list)
-
- c = template.RequestContext(request, {
- 'title': _('Change %s') % force_unicode(opts.verbose_name),
- 'form': form,
- 'object_id': object_id,
- 'original': manipulator.original_object,
- 'is_popup': '_popup' in request.REQUEST,
- })
- return render_change_form(model, manipulator, c, change=True)
-change_stage = staff_member_required(never_cache(change_stage))
-
-def _nest_help(obj, depth, val):
- current = obj
- for i in range(depth):
- current = current[-1]
- current.append(val)
-
-def _get_deleted_objects(deleted_objects, perms_needed, user, obj, opts, current_depth):
- "Helper function that recursively populates deleted_objects."
- nh = _nest_help # Bind to local variable for performance
- if current_depth > 16:
- return # Avoid recursing too deep.
- opts_seen = []
- for related in opts.get_all_related_objects():
- if related.opts in opts_seen:
- continue
- opts_seen.append(related.opts)
- rel_opts_name = related.get_accessor_name()
- if isinstance(related.field.rel, models.OneToOneRel):
- try:
- sub_obj = getattr(obj, rel_opts_name)
- except ObjectDoesNotExist:
- pass
- else:
- if related.opts.admin:
- p = '%s.%s' % (related.opts.app_label, related.opts.get_delete_permission())
- if not user.has_perm(p):
- perms_needed.add(related.opts.verbose_name)
- # We don't care about populating deleted_objects now.
- continue
- if related.field.rel.edit_inline or not related.opts.admin:
- # Don't display link to edit, because it either has no
- # admin or is edited inline.
- nh(deleted_objects, current_depth, [mark_safe(u'%s: %s' % (force_unicode(capfirst(related.opts.verbose_name)), sub_obj)), []])
- else:
- # Display a link to the admin page.
- nh(deleted_objects, current_depth, [mark_safe(u'%s: <a href="../../../../%s/%s/%s/">%s</a>' %
- (escape(force_unicode(capfirst(related.opts.verbose_name))),
- related.opts.app_label,
- related.opts.object_name.lower(),
- sub_obj._get_pk_val(), sub_obj)), []])
- _get_deleted_objects(deleted_objects, perms_needed, user, sub_obj, related.opts, current_depth+2)
- else:
- has_related_objs = False
- for sub_obj in getattr(obj, rel_opts_name).all():
- has_related_objs = True
- if related.field.rel.edit_inline or not related.opts.admin:
- # Don't display link to edit, because it either has no
- # admin or is edited inline.
- nh(deleted_objects, current_depth, [u'%s: %s' % (force_unicode(capfirst(related.opts.verbose_name)), escape(sub_obj)), []])
- else:
- # Display a link to the admin page.
- nh(deleted_objects, current_depth, [mark_safe(u'%s: <a href="../../../../%s/%s/%s/">%s</a>' % \
- (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))), []])
- _get_deleted_objects(deleted_objects, perms_needed, user, sub_obj, related.opts, current_depth+2)
- # If there were related objects, and the user doesn't have
- # permission to delete them, add the missing perm to perms_needed.
- if related.opts.admin and has_related_objs:
- p = '%s.%s' % (related.opts.app_label, related.opts.get_delete_permission())
- if not user.has_perm(p):
- perms_needed.add(related.opts.verbose_name)
- for related in opts.get_all_related_many_to_many_objects():
- if related.opts in opts_seen:
- continue
- opts_seen.append(related.opts)
- rel_opts_name = related.get_accessor_name()
- has_related_objs = False
-
- # related.get_accessor_name() could return None for symmetrical relationships
- if rel_opts_name:
- rel_objs = getattr(obj, rel_opts_name, None)
- if rel_objs:
- has_related_objs = True
-
- if has_related_objs:
- for sub_obj in rel_objs.all():
- if related.field.rel.edit_inline or not related.opts.admin:
- # Don't display link to edit, because it either has no
- # admin or is edited inline.
- nh(deleted_objects, current_depth, [_('One or more %(fieldname)s in %(name)s: %(obj)s') % \
- {'fieldname': force_unicode(related.field.verbose_name), 'name': force_unicode(related.opts.verbose_name), 'obj': escape(sub_obj)}, []])
- else:
- # Display a link to the admin page.
- nh(deleted_objects, current_depth, [
- 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))}) + \
- (u' <a href="../../../../%s/%s/%s/">%s</a>' % \
- (related.opts.app_label, related.opts.module_name, sub_obj._get_pk_val(), escape(sub_obj)))), []])
- # If there were related objects, and the user doesn't have
- # permission to change them, add the missing perm to perms_needed.
- if related.opts.admin and has_related_objs:
- p = u'%s.%s' % (related.opts.app_label, related.opts.get_change_permission())
- if not user.has_perm(p):
- perms_needed.add(related.opts.verbose_name)
-
-def delete_stage(request, app_label, model_name, object_id):
- model = models.get_model(app_label, model_name)
- object_id = unquote(object_id)
- if model is None:
- raise Http404("App %r, model %r, not found" % (app_label, model_name))
- opts = model._meta
- if not request.user.has_perm(app_label + '.' + opts.get_delete_permission()):
- raise PermissionDenied
- obj = get_object_or_404(model, pk=object_id)
-
- # Populate deleted_objects, a data structure of all related objects that
- # will also be deleted.
- deleted_objects = [mark_safe(u'%s: <a href="../../%s/">%s</a>' % (escape(force_unicode(capfirst(opts.verbose_name))), force_unicode(object_id), escape(obj))), []]
- perms_needed = set()
- _get_deleted_objects(deleted_objects, perms_needed, request.user, obj, opts, 1)
-
- if request.POST: # The user has already confirmed the deletion.
- if perms_needed:
- raise PermissionDenied
- obj_display = force_unicode(obj)
- obj.delete()
- LogEntry.objects.log_action(request.user.id, ContentType.objects.get_for_model(model).id, object_id, obj_display, DELETION)
- request.user.message_set.create(message=_('The %(name)s "%(obj)s" was deleted successfully.') % {'name': force_unicode(opts.verbose_name), 'obj': obj_display})
- return HttpResponseRedirect("../../")
- extra_context = {
- "title": _("Are you sure?"),
- "object_name": force_unicode(opts.verbose_name),
- "object": obj,
- "deleted_objects": deleted_objects,
- "perms_lacking": perms_needed,
- "opts": model._meta,
- }
- return render_to_response(["admin/%s/%s/delete_confirmation.html" % (app_label, opts.object_name.lower() ),
- "admin/%s/delete_confirmation.html" % app_label ,
- "admin/delete_confirmation.html"], extra_context, context_instance=template.RequestContext(request))
-delete_stage = staff_member_required(never_cache(delete_stage))
-
-def history(request, app_label, model_name, object_id):
- model = models.get_model(app_label, model_name)
- object_id = unquote(object_id)
- if model is None:
- raise Http404("App %r, model %r, not found" % (app_label, model_name))
- action_list = LogEntry.objects.filter(object_id=object_id,
- content_type__id__exact=ContentType.objects.get_for_model(model).id).select_related().order_by('action_time')
- # If no history was found, see whether this object even exists.
- obj = get_object_or_404(model, pk=object_id)
- extra_context = {
- 'title': _('Change history: %s') % obj,
- 'action_list': action_list,
- 'module_name': force_unicode(capfirst(model._meta.verbose_name_plural)),
- 'object': obj,
- }
- return render_to_response(["admin/%s/%s/object_history.html" % (app_label, model._meta.object_name.lower()),
- "admin/%s/object_history.html" % app_label ,
- "admin/object_history.html"], extra_context, context_instance=template.RequestContext(request))
-history = staff_member_required(never_cache(history))
-
class ChangeList(object):
- def __init__(self, request, model):
+ def __init__(self, request, model, list_display, list_display_links, list_filter, date_hierarchy, search_fields, list_select_related, list_per_page, model_admin):
self.model = model
self.opts = model._meta
self.lookup_opts = self.opts
- self.manager = self.opts.admin.manager
+ self.root_query_set = model_admin.queryset(request)
+ self.list_display = list_display
+ self.list_display_links = list_display_links
+ self.list_filter = list_filter
+ self.date_hierarchy = date_hierarchy
+ self.search_fields = search_fields
+ self.list_select_related = list_select_related
+ self.list_per_page = list_per_page
+ self.model_admin = model_admin
# Get search parameters from the query string.
try:
@@ -580,17 +63,16 @@ class ChangeList(object):
self.query = request.GET.get(SEARCH_VAR, '')
self.query_set = self.get_query_set()
self.get_results(request)
- 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))
+ 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))
self.filter_specs, self.has_filters = self.get_filters(request)
self.pk_attname = self.lookup_opts.pk.attname
def get_filters(self, request):
filter_specs = []
- if self.lookup_opts.admin.list_filter and not self.opts.one_to_one_field:
- filter_fields = [self.lookup_opts.get_field(field_name) \
- for field_name in self.lookup_opts.admin.list_filter]
+ if self.list_filter and not self.opts.one_to_one_field:
+ filter_fields = [self.lookup_opts.get_field(field_name) for field_name in self.list_filter]
for f in filter_fields:
- spec = FilterSpec.create(f, request, self.params, self.model)
+ spec = FilterSpec.create(f, request, self.params, self.model, self.model_admin)
if spec and spec.has_output():
filter_specs.append(spec)
return filter_specs, bool(filter_specs)
@@ -604,15 +86,15 @@ class ChangeList(object):
if k.startswith(r):
del p[k]
for k, v in new_params.items():
- if k in p and v is None:
- del p[k]
- elif v is not None:
+ if v is None:
+ if k in p:
+ del p[k]
+ else:
p[k] = v
- return mark_safe('?' + '&amp;'.join([u'%s=%s' % (k, v) for k, v in p.items()]).replace(' ', '%20'))
+ return '?%s' % urlencode(p)
def get_results(self, request):
- paginator = Paginator(self.query_set, self.lookup_opts.admin.list_per_page)
-
+ paginator = Paginator(self.query_set, self.list_per_page)
# Get the number of objects, with admin filters applied.
try:
result_count = paginator.count
@@ -630,10 +112,10 @@ class ChangeList(object):
if not self.query_set.query.where:
full_result_count = result_count
else:
- full_result_count = self.manager.count()
+ full_result_count = self.root_query_set.count()
can_show_all = result_count <= MAX_SHOW_ALL_ALLOWED
- multi_page = result_count > self.lookup_opts.admin.list_per_page
+ multi_page = result_count > self.list_per_page
# Get the list of objects to display on this page.
if (self.show_all and can_show_all) or not multi_page:
@@ -657,7 +139,7 @@ class ChangeList(object):
# options, then check the object's default ordering. If neither of
# those exist, order descending by ID by default. Finally, look for
# manually-specified ordering from the query string.
- ordering = lookup_opts.admin.ordering or lookup_opts.ordering or ['-' + lookup_opts.pk.name]
+ ordering = self.model_admin.ordering or lookup_opts.ordering or ['-' + lookup_opts.pk.name]
if ordering[0].startswith('-'):
order_field, order_type = ordering[0][1:], 'desc'
@@ -665,14 +147,14 @@ class ChangeList(object):
order_field, order_type = ordering[0], 'asc'
if ORDER_VAR in params:
try:
- field_name = lookup_opts.admin.list_display[int(params[ORDER_VAR])]
+ field_name = self.list_display[int(params[ORDER_VAR])]
try:
f = lookup_opts.get_field(field_name)
except models.FieldDoesNotExist:
- # see if field_name is a name of a non-field
- # that allows sorting
+ # See whether field_name is a name of a non-field
+ # that allows sorting.
try:
- attr = getattr(lookup_opts.admin.manager.model, field_name)
+ attr = getattr(self.model, field_name)
order_field = attr.admin_order_field
except AttributeError:
pass
@@ -686,7 +168,7 @@ class ChangeList(object):
return order_field, order_type
def get_query_set(self):
- qs = self.manager.get_query_set()
+ qs = self.root_query_set
lookup_params = self.params.copy() # a dictionary of the query string
for i in (ALL_VAR, ORDER_VAR, ORDER_TYPE_VAR, SEARCH_VAR, IS_POPUP_VAR):
if i in lookup_params:
@@ -703,10 +185,10 @@ class ChangeList(object):
# Use select_related() if one of the list_display options is a field
# with a relationship.
- if self.lookup_opts.admin.list_select_related:
+ if self.list_select_related:
qs = qs.select_related()
else:
- for field_name in self.lookup_opts.admin.list_display:
+ for field_name in self.list_display:
try:
f = self.lookup_opts.get_field(field_name)
except models.FieldDoesNotExist:
@@ -731,13 +213,17 @@ class ChangeList(object):
else:
return "%s__icontains" % field_name
- if self.lookup_opts.admin.search_fields and self.query:
+ if self.search_fields and self.query:
for bit in self.query.split():
- or_queries = [models.Q(**{construct_search(field_name): bit}) for field_name in self.lookup_opts.admin.search_fields]
+ or_queries = [models.Q(**{construct_search(field_name): bit}) for field_name in self.search_fields]
other_qs = QuerySet(self.model)
other_qs.dup_select_related(qs)
other_qs = other_qs.filter(reduce(operator.or_, or_queries))
qs = qs & other_qs
+ for field_name in self.search_fields:
+ if '__' in field_name:
+ qs = qs.distinct()
+ break
if self.opts.one_to_one_field:
qs = qs.complex_filter(self.opts.one_to_one_field.rel.limit_choices_to)
@@ -746,31 +232,3 @@ class ChangeList(object):
def url_for_result(self, result):
return "%s/" % quote(getattr(result, self.pk_attname))
-
-def change_list(request, app_label, model_name):
- model = models.get_model(app_label, model_name)
- if model is None:
- raise Http404("App %r, model %r, not found" % (app_label, model_name))
- if not request.user.has_perm(app_label + '.' + model._meta.get_change_permission()):
- raise PermissionDenied
- try:
- cl = ChangeList(request, model)
- except IncorrectLookupParameters:
- # Wacky lookup parameters were given, so redirect to the main
- # changelist page, without parameters, and pass an 'invalid=1'
- # parameter via the query string. If wacky parameters were given and
- # the 'invalid=1' parameter was already in the query string, something
- # is screwed up with the database, so display an error page.
- if ERROR_FLAG in request.GET.keys():
- return render_to_response('admin/invalid_setup.html', {'title': _('Database error')})
- return HttpResponseRedirect(request.path + '?' + ERROR_FLAG + '=1')
- c = template.RequestContext(request, {
- 'title': cl.title,
- 'is_popup': cl.is_popup,
- 'cl': cl,
- })
- c.update({'has_add_permission': c['perms'][app_label][cl.opts.get_add_permission()]}),
- return render_to_response(['admin/%s/%s/change_list.html' % (app_label, cl.opts.object_name.lower()),
- 'admin/%s/change_list.html' % app_label,
- 'admin/change_list.html'], context_instance=c)
-change_list = staff_member_required(never_cache(change_list))