diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py index 6373f6028c..5259e06324 100644 --- a/django/contrib/admin/options.py +++ b/django/contrib/admin/options.py @@ -4,6 +4,7 @@ from django.newforms.formsets import all_valid from django.newforms.models import inline_formset from django.newforms.widgets import Media, MediaDefiningClass from django.contrib.admin import widgets +from django.contrib.admin.util import get_deleted_objects from django.core.exceptions import ImproperlyConfigured, PermissionDenied from django.db import models from django.http import Http404, HttpResponse, HttpResponseRedirect @@ -565,7 +566,7 @@ class ModelAdmin(BaseModelAdmin): # will also be deleted. deleted_objects = [u'%s: %s' % (force_unicode(capfirst(opts.verbose_name)), object_id, escape(str(obj))), []] perms_needed = sets.Set() - _get_deleted_objects(deleted_objects, perms_needed, request.user, obj, opts, 1) + 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: diff --git a/django/contrib/admin/util.py b/django/contrib/admin/util.py new file mode 100644 index 0000000000..29b7668a9a --- /dev/null +++ b/django/contrib/admin/util.py @@ -0,0 +1,96 @@ +from django.core.exceptions import ObjectDoesNotExist +from django.db import models +from django.utils.html import escape +from django.utils.text import capfirst +from django.utils.encoding import force_unicode + +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, [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, [u'%s: %s' % \ + (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, [u'%s: %s' % \ + (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, [ + (_('One or more %(fieldname)s in %(name)s:') % {'fieldname': force_unicode(related.field.verbose_name), 'name': force_unicode(related.opts.verbose_name)}) + \ + (u' %s' % \ + (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) diff --git a/django/contrib/admin/views/main.py b/django/contrib/admin/views/main.py index 2366123415..c6be12a0b5 100644 --- a/django/contrib/admin/views/main.py +++ b/django/contrib/admin/views/main.py @@ -4,16 +4,13 @@ from django.contrib.admin.options import IncorrectLookupParameters 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 ObjectDoesNotExist from django.core.paginator import ObjectPaginator, InvalidPage -from django.shortcuts import get_object_or_404, render_to_response +from django.shortcuts import render_to_response from django.db import models from django.db.models.query import handle_legacy_orderlist, QuerySet from django.http import Http404 -from django.utils.html import escape -from django.utils.text import capfirst from django.utils.encoding import force_unicode, smart_str -from django.utils.translation import ugettext as _ +from django.utils.translation import ugettext import operator try: @@ -140,99 +137,9 @@ def render_change_form(model_admin, model, manipulator, context, add=False, chan "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)) + return render_to_response('admin/index.html', {'title': ugettext('Site administration')}, context_instance=template.RequestContext(request)) index = staff_member_required(never_cache(index)) -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, [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, [u'%s: %s' % \ - (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, [u'%s: %s' % \ - (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, [ - (_('One or more %(fieldname)s in %(name)s:') % {'fieldname': force_unicode(related.field.verbose_name), 'name': force_unicode(related.opts.verbose_name)}) + \ - (u' %s' % \ - (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) class ChangeList(object): def __init__(self, request, model, list_display, list_display_links, list_filter, date_hierarchy, search_fields, list_select_related, list_per_page, model_admin): @@ -266,7 +173,7 @@ 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