diff options
Diffstat (limited to 'django/contrib/admin')
| -rw-r--r-- | django/contrib/admin/media/css/forms.css | 5 | ||||
| -rw-r--r-- | django/contrib/admin/media/js/urlify.js | 2 | ||||
| -rw-r--r-- | django/contrib/admin/row_level_perm_manipulator.py | 2 | ||||
| -rw-r--r-- | django/contrib/admin/templates/admin/auth/user/change_password.html | 52 | ||||
| -rw-r--r-- | django/contrib/admin/templatetags/admin_list.py | 51 | ||||
| -rw-r--r-- | django/contrib/admin/templatetags/admin_modify.py | 25 | ||||
| -rw-r--r-- | django/contrib/admin/templatetags/adminapplist.py | 4 | ||||
| -rw-r--r-- | django/contrib/admin/templatetags/log.py | 13 | ||||
| -rw-r--r-- | django/contrib/admin/urls.py | 2 | ||||
| -rw-r--r-- | django/contrib/admin/views/auth.py | 41 | ||||
| -rw-r--r-- | django/contrib/admin/views/decorators.py | 12 | ||||
| -rw-r--r-- | django/contrib/admin/views/doc.py | 9 | ||||
| -rw-r--r-- | django/contrib/admin/views/main.py | 62 | ||||
| -rw-r--r-- | django/contrib/admin/views/row_level_permissions.py | 3 |
14 files changed, 202 insertions, 81 deletions
diff --git a/django/contrib/admin/media/css/forms.css b/django/contrib/admin/media/css/forms.css index 01a2937b7b..dd4c2507b7 100644 --- a/django/contrib/admin/media/css/forms.css +++ b/django/contrib/admin/media/css/forms.css @@ -26,7 +26,7 @@ form .aligned p, form .aligned ul { margin-left:7em; padding-left:30px; } form .aligned table p { margin-left:0; padding-left:0; } form .aligned p.help { padding-left:38px; } .aligned .vCheckboxLabel { float:none !important; display:inline; padding-left:4px; } -.colM .aligned .vLargeTextField, colM .aligned .vXMLLargeTextField { width:610px; } +.colM .aligned .vLargeTextField, .colM .aligned .vXMLLargeTextField { width:610px; } .checkbox-row p.help { margin-left:0; padding-left:0 !important; } /* WIDE FIELDSETS */ @@ -66,4 +66,5 @@ fieldset.monospace textarea { font-family:"Bitstream Vera Sans Mono",Monaco,"Cou .paginate-first, .paginate-last { padding: 2px 6px; border: 1px solid #ddd; font-weight: bold; } .paginate-previous, .paginate-next { padding: 2px 3px; border: 1px solid #ddd; } .paginate-link { padding: 2px 4px; border: 1px solid #ddd; } -.paginate-current { padding: 2px 4px; border: 1px solid #ddd; font-weight: bold; background:#417690; color:#f4f379; }
\ No newline at end of file +.paginate-current { padding: 2px 4px; border: 1px solid #ddd; font-weight: bold; background:#417690; color:#f4f379; } +.module table .vPositiveSmallIntegerField { width:2.2em; } diff --git a/django/contrib/admin/media/js/urlify.js b/django/contrib/admin/media/js/urlify.js index 90d11c435a..9b87113628 100644 --- a/django/contrib/admin/media/js/urlify.js +++ b/django/contrib/admin/media/js/urlify.js @@ -7,7 +7,7 @@ function URLify(s, num_chars) { "with"]; r = new RegExp('\\b(' + removelist.join('|') + ')\\b', 'gi'); s = s.replace(r, ''); - s = s.replace(/[^-A-Z0-9\s]/gi, ''); // remove unneeded chars + s = s.replace(/[^-\w\s]/g, ''); // remove unneeded chars s = s.replace(/^\s+|\s+$/g, ''); // trim leading/trailing spaces s = s.replace(/[-\s]+/g, '-'); // convert spaces to hyphens s = s.toLowerCase(); // convert to lowercase diff --git a/django/contrib/admin/row_level_perm_manipulator.py b/django/contrib/admin/row_level_perm_manipulator.py index efedc46041..cde087c5a3 100644 --- a/django/contrib/admin/row_level_perm_manipulator.py +++ b/django/contrib/admin/row_level_perm_manipulator.py @@ -1,4 +1,4 @@ -from django import forms +from django import oldforms as forms from django.contrib.contenttypes.models import ContentType from django.http import Http404, HttpResponse, HttpResponseRedirect from django.contrib.auth.models import User, Group, Permission, RowLevelPermission diff --git a/django/contrib/admin/templates/admin/auth/user/change_password.html b/django/contrib/admin/templates/admin/auth/user/change_password.html new file mode 100644 index 0000000000..3d359ecf8f --- /dev/null +++ b/django/contrib/admin/templates/admin/auth/user/change_password.html @@ -0,0 +1,52 @@ +{% extends "admin/base_site.html" %} +{% load i18n admin_modify adminmedia %} +{% block extrahead %}{{ block.super }} +<script type="text/javascript" src="../../../../jsi18n/"></script> +{% for js in javascript_imports %}{% include_admin_script js %}{% endfor %} +{% endblock %} +{% block stylesheet %}{% admin_media_prefix %}css/forms.css{% endblock %} +{% block bodyclass %}{{ opts.app_label }}-{{ opts.object_name.lower }} change-form{% endblock %} +{% block userlinks %}<a href="../../../../doc/">{% trans 'Documentation' %}</a> / <a href="../../../password_change/">{% trans 'Change password' %}</a> / <a href="../../../logout/">{% trans 'Log out' %}</a>{% endblock %} +{% block breadcrumbs %}{% if not is_popup %} +<div class="breadcrumbs"> + <a href="../../../../">{% trans "Home" %}</a> › + <a href="../../">{{ opts.verbose_name_plural|capfirst|escape }}</a> › + <a href="../">{{ original|truncatewords:"18"|escape }}</a> › + {% trans 'Change password' %} +</div> +{% endif %}{% endblock %} +{% block content %}<div id="content-main"> +<form action="{{ form_url }}" method="post" id="{{ opts.module_name }}_form">{% block form_top %}{% endblock %} +<div> +{% if is_popup %}<input type="hidden" name="_popup" value="1" />{% endif %} +{% if form.error_dict %} + <p class="errornote"> + {% blocktrans count form.error_dict.items|length as counter %}Please correct the error below.{% plural %}Please correct the errors below.{% endblocktrans %} + </p> +{% endif %} + +<p>{% blocktrans with original.username|escape as username %}Enter a new password for the user <strong>{{ username }}</strong>.{% endblocktrans %}</p> + +<fieldset class="module aligned"> + +<div class="form-row"> + {{ form.password1.html_error_list }} + <label for="id_password1" class="required">{% trans 'Password' %}:</label> {{ form.password1 }} +</div> + +<div class="form-row"> + {{ form.password2.html_error_list }} + <label for="id_password2" class="required">{% trans 'Password (again)' %}:</label> {{ form.password2 }} + <p class="help">{% trans 'Enter the same password as above, for verification.' %}</p> +</div> + +</fieldset> + +<div class="submit-row"> +<input type="submit" value="{% trans 'Change password' %}" class="default" /> +</div> + +<script type="text/javascript">document.getElementById("{{ first_form_field_id }}").focus();</script> +</div> +</form></div> +{% endblock %} diff --git a/django/contrib/admin/templatetags/admin_list.py b/django/contrib/admin/templatetags/admin_list.py index 430fe2781c..e4d91c0174 100644 --- a/django/contrib/admin/templatetags/admin_list.py +++ b/django/contrib/admin/templatetags/admin_list.py @@ -72,6 +72,7 @@ def result_headers(cl): for i, field_name in enumerate(lookup_opts.admin.list_display): try: f = lookup_opts.get_field(field_name) + admin_order_field = None except models.FieldDoesNotExist: # For non-field list_display values, check for the function # attribute "short_description". If that doesn't exist, fall @@ -84,22 +85,36 @@ def result_headers(cl): header = attr.short_description except AttributeError: header = field_name.replace('_', ' ') - # Non-field list_display values don't get ordering capability. - yield {"text": header} + + # It is a non-field, but perhaps one that is sortable + admin_order_field = getattr(getattr(cl.model, field_name), "admin_order_field", None) + if not admin_order_field: + yield {"text": header} + continue + + # So this _is_ a sortable non-field. Go to the yield + # after the else clause. else: if isinstance(f.rel, models.ManyToOneRel) and f.null: yield {"text": f.verbose_name} + continue else: - th_classes = [] - new_order_type = 'asc' - if field_name == cl.order_field: - th_classes.append('sorted %sending' % cl.order_type.lower()) - new_order_type = {'asc': 'desc', 'desc': 'asc'}[cl.order_type.lower()] + header = f.verbose_name + + th_classes = [] + new_order_type = 'asc' + if field_name == cl.order_field or admin_order_field == cl.order_field: + th_classes.append('sorted %sending' % cl.order_type.lower()) + new_order_type = {'asc': 'desc', 'desc': 'asc'}[cl.order_type.lower()] - yield {"text": f.verbose_name, - "sortable": True, - "url": cl.get_query_string({ORDER_VAR: i, ORDER_TYPE_VAR: new_order_type}), - "class_attrib": (th_classes and ' class="%s"' % ' '.join(th_classes) or '')} + yield {"text": header, + "sortable": True, + "url": cl.get_query_string({ORDER_VAR: i, ORDER_TYPE_VAR: new_order_type}), + "class_attrib": (th_classes and ' class="%s"' % ' '.join(th_classes) or '')} + +def _boolean_icon(field_val): + BOOLEAN_MAPPING = {True: 'yes', False: 'no', None: 'unknown'} + return '<img src="%simg/admin/icon-%s.gif" alt="%s" />' % (settings.ADMIN_MEDIA_PREFIX, BOOLEAN_MAPPING[field_val], field_val) def items_for_result(cl, result): first = True @@ -116,9 +131,14 @@ def items_for_result(cl, result): try: attr = getattr(result, field_name) allow_tags = getattr(attr, 'allow_tags', False) + boolean = getattr(attr, 'boolean', False) if callable(attr): attr = attr() - result_repr = str(attr) + if boolean: + allow_tags = True + result_repr = _boolean_icon(attr) + else: + result_repr = str(attr) except (AttributeError, ObjectDoesNotExist): result_repr = EMPTY_CHANGELIST_VALUE else: @@ -149,10 +169,9 @@ def items_for_result(cl, result): row_class = ' class="nowrap"' # Booleans are special: We use images. elif isinstance(f, models.BooleanField) or isinstance(f, models.NullBooleanField): - BOOLEAN_MAPPING = {True: 'yes', False: 'no', None: 'unknown'} - result_repr = '<img src="%simg/admin/icon-%s.gif" alt="%s" />' % (settings.ADMIN_MEDIA_PREFIX, BOOLEAN_MAPPING[field_val], field_val) - # FloatFields are special: Zero-pad the decimals. - elif isinstance(f, models.FloatField): + result_repr = _boolean_icon(field_val) + # DecimalFields are special: Zero-pad the decimals. + elif isinstance(f, models.DecimalField): if field_val is not None: result_repr = ('%%.%sf' % f.decimal_places) % field_val else: diff --git a/django/contrib/admin/templatetags/admin_modify.py b/django/contrib/admin/templatetags/admin_modify.py index 85479d3756..81441c1fbb 100644 --- a/django/contrib/admin/templatetags/admin_modify.py +++ b/django/contrib/admin/templatetags/admin_modify.py @@ -11,6 +11,7 @@ import re register = template.Library() word_re = re.compile('[A-Z][a-z]+') +absolute_url_re = re.compile(r'^(?:http(?:s)?:/)?/', re.IGNORECASE) def class_name_to_underscored(name): return '_'.join([s.lower() for s in word_re.findall(name)[:-1]]) @@ -18,18 +19,19 @@ def class_name_to_underscored(name): def include_admin_script(script_path): """ Returns an HTML script element for including a script from the admin - media url. + media url (or other location if an absolute url is given). Example usage:: - {% include_admin_script js/calendar.js %} + {% include_admin_script "js/calendar.js" %} could return:: <script type="text/javascript" src="/media/admin/js/calendar.js"> """ - - return '<script type="text/javascript" src="%s%s"></script>' % (settings.ADMIN_MEDIA_PREFIX, script_path) + if not absolute_url_re.match(script_path): + script_path = '%s%s' % (settings.ADMIN_MEDIA_PREFIX, script_path) + return '<script type="text/javascript" src="%s"></script>' % script_path include_admin_script = register.simple_tag(include_admin_script) def submit_row(context): @@ -72,7 +74,7 @@ class FieldWidgetNode(template.Node): self.bound_field_var = bound_field_var def get_nodelist(cls, klass): - if not cls.nodelists.has_key(klass): + if klass not in cls.nodelists: try: field_class_name = klass.__name__ template_name = "widget/%s.html" % class_name_to_underscored(field_class_name) @@ -92,15 +94,15 @@ class FieldWidgetNode(template.Node): return cls.nodelists[klass] get_nodelist = classmethod(get_nodelist) - def render(self, context): + def iter_render(self, context): bound_field = template.resolve_variable(self.bound_field_var, context) context.push() context['bound_field'] = bound_field - output = self.get_nodelist(bound_field.field.__class__).render(context) + for chunk in self.get_nodelist(bound_field.field.__class__).iter_render(context): + yield chunk context.pop() - return output class FieldWrapper(object): def __init__(self, field ): @@ -165,7 +167,7 @@ class EditInlineNode(template.Node): def __init__(self, rel_var): self.rel_var = rel_var - def render(self, context): + def iter_render(self, context): relation = template.resolve_variable(self.rel_var, context) context.push() if relation.field.rel.edit_inline == models.TABULAR: @@ -177,10 +179,9 @@ class EditInlineNode(template.Node): original = context.get('original', None) bound_related_object = relation.bind(context['form'], original, bound_related_object_class) context['bound_related_object'] = bound_related_object - t = loader.get_template(bound_related_object.template_name()) - output = t.render(context) + for chunk in loader.get_template(bound_related_object.template_name()).iter_render(context): + yield chunk context.pop() - return output def output_all(form_fields): return ''.join([str(f) for f in form_fields]) diff --git a/django/contrib/admin/templatetags/adminapplist.py b/django/contrib/admin/templatetags/adminapplist.py index 6754632fba..f9c140433f 100644 --- a/django/contrib/admin/templatetags/adminapplist.py +++ b/django/contrib/admin/templatetags/adminapplist.py @@ -7,7 +7,7 @@ class AdminApplistNode(template.Node): def __init__(self, varname): self.varname = varname - def render(self, context): + def iter_render(self, context): from django.db import models from django.utils.text import capfirst app_list = [] @@ -56,7 +56,7 @@ class AdminApplistNode(template.Node): 'models': model_list, }) context[self.varname] = app_list - return '' + return () def get_admin_app_list(parser, token): """ diff --git a/django/contrib/admin/templatetags/log.py b/django/contrib/admin/templatetags/log.py index 5caba2b795..96db2373b4 100644 --- a/django/contrib/admin/templatetags/log.py +++ b/django/contrib/admin/templatetags/log.py @@ -10,11 +10,14 @@ class AdminLogNode(template.Node): def __repr__(self): return "<GetAdminLog Node>" - def render(self, context): - if self.user is not None and not self.user.isdigit(): - self.user = context[self.user].id - context[self.varname] = LogEntry.objects.filter(user__id__exact=self.user).select_related()[:self.limit] - return '' + def iter_render(self, context): + if self.user is None: + context[self.varname] = LogEntry.objects.all().select_related()[:self.limit] + else: + if not self.user.isdigit(): + self.user = context[self.user].id + context[self.varname] = LogEntry.objects.filter(user__id__exact=self.user).select_related()[:self.limit] + return () class DoGetAdminLog: """ diff --git a/django/contrib/admin/urls.py b/django/contrib/admin/urls.py index 3d029b7e45..67a481765a 100644 --- a/django/contrib/admin/urls.py +++ b/django/contrib/admin/urls.py @@ -35,6 +35,8 @@ urlpatterns = patterns('', # "Add user" -- a special-case view ('^auth/user/add/$', 'django.contrib.admin.views.auth.user_add_stage'), + # "Change user password" -- another special-case view + ('^auth/user/(\d+)/password/$', 'django.contrib.admin.views.auth.user_change_password'), # Add/change/delete/history ('^([^/]+)/([^/]+)/$', 'django.contrib.admin.views.main.change_list'), diff --git a/django/contrib/admin/views/auth.py b/django/contrib/admin/views/auth.py index 52bf3bcde8..206e3eb7d4 100644 --- a/django/contrib/admin/views/auth.py +++ b/django/contrib/admin/views/auth.py @@ -1,10 +1,11 @@ from django.contrib.admin.views.decorators import staff_member_required -from django.contrib.auth.forms import UserCreationForm +from django.contrib.auth.forms import UserCreationForm, AdminPasswordChangeForm from django.contrib.auth.models import User from django.core.exceptions import PermissionDenied from django import oldforms, template -from django.shortcuts import render_to_response +from django.shortcuts import render_to_response, get_object_or_404 from django.http import HttpResponseRedirect +from django.utils.html import escape def user_add_stage(request): if not request.user.has_perm('auth.change_user'): @@ -16,7 +17,7 @@ def user_add_stage(request): if not errors: new_user = manipulator.save(new_data) msg = _('The %(name)s "%(obj)s" was added successfully.') % {'name': 'user', 'obj': new_user} - if request.POST.has_key("_addanother"): + if "_addanother" in request.POST: request.user.message_set.create(message=msg) return HttpResponseRedirect(request.path) else: @@ -28,7 +29,7 @@ def user_add_stage(request): return render_to_response('admin/auth/user/add_form.html', { 'title': _('Add user'), 'form': form, - 'is_popup': request.REQUEST.has_key('_popup'), + 'is_popup': '_popup' in request.REQUEST, 'add': True, 'change': False, 'has_add_permission': True, @@ -43,3 +44,35 @@ def user_add_stage(request): 'username_help_text': User._meta.get_field('username').help_text, }, context_instance=template.RequestContext(request)) user_add_stage = staff_member_required(user_add_stage) + +def user_change_password(request, id): + if not request.user.has_perm('auth.change_user'): + raise PermissionDenied + user = get_object_or_404(User, pk=id) + manipulator = AdminPasswordChangeForm(user) + if request.method == 'POST': + new_data = request.POST.copy() + errors = manipulator.get_validation_errors(new_data) + if not errors: + new_user = manipulator.save(new_data) + msg = _('Password changed successfully.') + request.user.message_set.create(message=msg) + return HttpResponseRedirect('..') + else: + errors = new_data = {} + form = oldforms.FormWrapper(manipulator, new_data, errors) + return render_to_response('admin/auth/user/change_password.html', { + 'title': _('Change password: %s') % escape(user.username), + 'form': form, + 'is_popup': '_popup' in request.REQUEST, + 'add': True, + 'change': False, + 'has_delete_permission': False, + 'has_change_permission': True, + 'has_absolute_url': False, + 'first_form_field_id': 'id_password1', + 'opts': User._meta, + 'original': user, + 'show_save': True, + }, context_instance=template.RequestContext(request)) +user_change_password = staff_member_required(user_change_password) diff --git a/django/contrib/admin/views/decorators.py b/django/contrib/admin/views/decorators.py index 9dfe651fe6..5389ca4dff 100644 --- a/django/contrib/admin/views/decorators.py +++ b/django/contrib/admin/views/decorators.py @@ -12,7 +12,7 @@ LOGIN_FORM_KEY = 'this_is_the_login_form' def _display_login_form(request, error_message=''): request.session.set_test_cookie() - if request.POST and request.POST.has_key('post_data'): + if request.POST and 'post_data' in request.POST: # User has failed login BUT has previously saved post data. post_data = request.POST['post_data'] elif request.POST: @@ -48,7 +48,7 @@ def staff_member_required(view_func): def _checklogin(request, *args, **kwargs): if request.user.is_authenticated() and request.user.is_staff: # The user is valid. Continue to the admin page. - if request.POST.has_key('post_data'): + if 'post_data' in request.POST: # User must have re-authenticated through a different window # or tab. request.POST = _decode_post_data(request.POST['post_data']) @@ -57,7 +57,7 @@ def staff_member_required(view_func): assert hasattr(request, 'session'), "The Django admin requires session middleware to be installed. Edit your MIDDLEWARE_CLASSES setting to insert 'django.contrib.sessions.middleware.SessionMiddleware'." # If this isn't already the login page, display it. - if not request.POST.has_key(LOGIN_FORM_KEY): + if LOGIN_FORM_KEY not in request.POST: if request.POST: message = _("Please log in again, because your session has expired. Don't worry: Your submission has been saved.") else: @@ -90,11 +90,9 @@ def staff_member_required(view_func): if user.is_active and user.is_staff: login(request, user) # TODO: set last_login with an event. - user.last_login = datetime.datetime.now() - user.save() - if request.POST.has_key('post_data'): + if 'post_data' in request.POST: post_data = _decode_post_data(request.POST['post_data']) - if post_data and not post_data.has_key(LOGIN_FORM_KEY): + if post_data and LOGIN_FORM_KEY not in post_data: # overwrite request.POST with the saved post_data, and continue request.POST = post_data request.user = user diff --git a/django/contrib/admin/views/doc.py b/django/contrib/admin/views/doc.py index 4b592acebd..6430252690 100644 --- a/django/contrib/admin/views/doc.py +++ b/django/contrib/admin/views/doc.py @@ -168,7 +168,7 @@ def model_detail(request, app_label, model_name): model = m break if model is None: - raise Http404, _("Model %r not found in app %r") % (model_name, app_label) + raise Http404, _("Model %(name)r not found in app %(label)r") % {'name': model_name, 'label': app_label} opts = model._meta @@ -180,7 +180,7 @@ def model_detail(request, app_label, model_name): if isinstance(field, models.ForeignKey): data_type = related_object_name = field.rel.to.__name__ app_label = field.rel.to._meta.app_label - verbose = utils.parse_rst((_("the related `%s.%s` object") % (app_label, data_type)), 'model', _('model:') + data_type) + verbose = utils.parse_rst((_("the related `%(label)s.%(type)s` object") % {'label': app_label, 'type': data_type}), 'model', _('model:') + data_type) else: data_type = get_readable_field_data_type(field) verbose = field.verbose_name @@ -211,7 +211,7 @@ def model_detail(request, app_label, model_name): # Gather related objects for rel in opts.get_all_related_objects(): - verbose = _("related `%s.%s` objects") % (rel.opts.app_label, rel.opts.object_name) + verbose = _("related `%(label)s.%(name)s` objects") % {'label': rel.opts.app_label, 'name': rel.opts.object_name} accessor = rel.get_accessor_name() fields.append({ 'name' : "%s.all" % accessor, @@ -294,10 +294,11 @@ DATA_TYPE_MAPPING = { 'CommaSeparatedIntegerField': _('Comma-separated integers'), 'DateField' : _('Date (without time)'), 'DateTimeField' : _('Date (with time)'), + 'DecimalField' : _('Decimal number'), 'EmailField' : _('E-mail address'), 'FileField' : _('File path'), 'FilePathField' : _('File path'), - 'FloatField' : _('Decimal number'), + 'FloatField' : _('Floating point number'), 'ForeignKey' : _('Integer'), 'ImageField' : _('File path'), 'IntegerField' : _('Integer'), diff --git a/django/contrib/admin/views/main.py b/django/contrib/admin/views/main.py index aa8b35bb96..1e99ea341b 100644 --- a/django/contrib/admin/views/main.py +++ b/django/contrib/admin/views/main.py @@ -46,8 +46,8 @@ 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 autoamtically - unquoted by the web browser. + quoting is slightly different so that it doesn't get automatically + unquoted by the Web browser. """ if type(s) != type(''): return s @@ -273,17 +273,17 @@ def add_stage(request, app_label, model_name, show_delete=False, form_url='', po delete=admin_opts.grant_delete_row_level_perm) # Here, we distinguish between different save types by checking for # the presence of keys in request.POST. - if request.POST.has_key("_continue"): + if "_continue" in request.POST: request.user.message_set.create(message=msg + ' ' + _("You may edit it again below.")) - if request.POST.has_key("_popup"): + if "_popup" in request.POST: post_url_continue += "?_popup=1" return HttpResponseRedirect(post_url_continue % pk_value) - if request.POST.has_key("_popup"): + if "_popup" in request.POST: if type(pk_value) is str: # Quote if string, so JavaScript doesn't think it's a variable. pk_value = '"%s"' % pk_value.replace('"', '\\"') return HttpResponse('<script type="text/javascript">opener.dismissAddAnotherPopup(window, %s, "%s");</script>' % \ (pk_value, str(new_object).replace('"', '\\"'))) - elif request.POST.has_key("_addanother"): + elif "_addanother" in request.POST: request.user.message_set.create(message=msg + ' ' + (_("You may add another %s below.") % opts.verbose_name)) return HttpResponseRedirect(request.path) else: @@ -304,7 +304,7 @@ def add_stage(request, app_label, model_name, show_delete=False, form_url='', po c = template.RequestContext(request, { 'title': _('Add %s') % opts.verbose_name, 'form': form, - 'is_popup': request.REQUEST.has_key('_popup'), + 'is_popup': '_popup' in request.REQUEST, 'show_delete': show_delete, }) @@ -329,7 +329,7 @@ def change_stage(request, app_label, model_name, object_id): if not request.user.has_perm(app_label + '.' + opts.get_change_permission(), object=manipulator.original_object): raise PermissionDenied - if request.POST and request.POST.has_key("_saveasnew"): + if request.POST and "_saveasnew" in request.POST: return add_stage(request, app_label, model_name, form_url='../../add/') if request.POST: @@ -367,16 +367,16 @@ def change_stage(request, app_label, model_name, object_id): delete=admin_opts.grant_delete_row_level_perm) msg = _('The %(name)s "%(obj)s" was changed successfully.') % {'name': opts.verbose_name, 'obj': new_object} - if request.POST.has_key("_continue"): + if "_continue" in request.POST: request.user.message_set.create(message=msg + ' ' + _("You may edit it again below.")) - if request.REQUEST.has_key('_popup'): + if '_popup' in request.REQUEST: return HttpResponseRedirect(request.path + "?_popup=1") else: return HttpResponseRedirect(request.path) - elif request.POST.has_key("_saveasnew"): + 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': opts.verbose_name, 'obj': new_object}) return HttpResponseRedirect("../%s/" % pk_value) - elif request.POST.has_key("_addanother"): + elif "_addanother" in request.POST: request.user.message_set.create(message=msg + ' ' + (_("You may add another %s below.") % opts.verbose_name)) return HttpResponseRedirect("../add/") else: @@ -416,7 +416,7 @@ def change_stage(request, app_label, model_name, object_id): 'form': form, 'object_id': object_id, 'original': manipulator.original_object, - 'is_popup': request.REQUEST.has_key('_popup'), + 'is_popup': '_popup' in request.REQUEST, 'object_has_row_level_permissions':opts.row_level_permissions, 'has_row_level_permissions':request.user.has_perm("auth.change_rowlevelpermission") and request.user.has_perm(opts.app_label+"."+opts.get_change_permission(), object=manipulator.original_object), }) @@ -488,9 +488,12 @@ def _get_deleted_objects(deleted_objects, perms_needed, user, obj, opts, current opts_seen.append(related.opts) rel_opts_name = related.get_accessor_name() has_related_objs = False - rel_objs = getattr(obj, rel_opts_name, None) - if rel_objs: - has_related_objs = True + + # 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(): @@ -585,12 +588,12 @@ class ChangeList(object): self.page_num = int(request.GET.get(PAGE_VAR, 0)) except ValueError: self.page_num = 0 - self.show_all = request.GET.has_key(ALL_VAR) - self.is_popup = request.GET.has_key(IS_POPUP_VAR) + self.show_all = ALL_VAR in request.GET + self.is_popup = IS_POPUP_VAR in request.GET self.params = dict(request.GET.items()) - if self.params.has_key(PAGE_VAR): + if PAGE_VAR in self.params: del self.params[PAGE_VAR] - if self.params.has_key(ERROR_FLAG): + if ERROR_FLAG in self.params: del self.params[ERROR_FLAG] self.order_field, self.order_type = self.get_ordering() @@ -621,7 +624,7 @@ class ChangeList(object): if k.startswith(r): del p[k] for k, v in new_params.items(): - if p.has_key(k) and v is None: + if k in p and v is None: del p[k] elif v is not None: p[k] = v @@ -687,18 +690,25 @@ class ChangeList(object): order_field, order_type = ordering[0][1:], 'desc' else: order_field, order_type = ordering[0], 'asc' - if params.has_key(ORDER_VAR): + if ORDER_VAR in params: try: + field_name = lookup_opts.admin.list_display[int(params[ORDER_VAR])] try: - f = lookup_opts.get_field(lookup_opts.admin.list_display[int(params[ORDER_VAR])]) + f = lookup_opts.get_field(field_name) except models.FieldDoesNotExist: - pass + # see if field_name is a name of a non-field + # that allows sorting + try: + attr = getattr(lookup_opts.admin.manager.model, field_name) + order_field = attr.admin_order_field + except IndexError: + pass else: if not isinstance(f.rel, models.ManyToOneRel) or not f.null: order_field = f.name except (IndexError, ValueError): pass # Invalid ordering specified. Just use the default. - if params.has_key(ORDER_TYPE_VAR) and params[ORDER_TYPE_VAR] in ('asc', 'desc'): + if ORDER_TYPE_VAR in params and params[ORDER_TYPE_VAR] in ('asc', 'desc'): order_type = params[ORDER_TYPE_VAR] return order_field, order_type @@ -715,7 +725,7 @@ class ChangeList(object): qs = self.manager.get_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 lookup_params.has_key(i): + if i in lookup_params: del lookup_params[i] # Apply lookup parameters from the query string. diff --git a/django/contrib/admin/views/row_level_permissions.py b/django/contrib/admin/views/row_level_permissions.py index a5ea239b51..e9bda73217 100644 --- a/django/contrib/admin/views/row_level_permissions.py +++ b/django/contrib/admin/views/row_level_permissions.py @@ -1,5 +1,6 @@ from django.contrib.admin import utils -from django import forms, template +from django import oldforms as forms +from django import template from django.shortcuts import render_to_response, get_object_or_404 from django.http import Http404, HttpResponse, HttpResponseRedirect from django.contrib.contenttypes.models import ContentType |
