From 1fab275061c992c07abc68b1d0f100e9099f967f Mon Sep 17 00:00:00 2001
From: Simon Charette <charette.s@gmail.com>
Date: Sat, 22 Jun 2013 21:48:09 -0400
Subject: [PATCH] Fixed #20642 -- Deprecated
`Option.get_(add|change|delete)_permission`.
Those methods were only used by `contrib.admin` internally and exclusively
related to `contrib.auth`. Since they were undocumented but used
in the wild the raised deprecation warning point to an also undocumented
alternative that lives in `contrib.auth`.
Also did some PEP8 and other cleanups in the affected modules.
---
django/contrib/admin/options.py | 100 ++++++++++++++++-------------
django/contrib/auth/__init__.py | 15 ++++-
django/contrib/auth/management/__init__.py | 12 ++--
django/db/models/options.py | 24 +++++++
docs/releases/1.6.txt | 7 ++
5 files changed, 106 insertions(+), 52 deletions(-)
diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py
index e9f4a43..c89dde5 100644
a
|
b
|
from functools import partial, reduce, update_wrapper
|
4 | 4 | |
5 | 5 | from django import forms |
6 | 6 | from django.conf import settings |
7 | | from django.forms.formsets import all_valid, DELETION_FIELD_NAME |
8 | | from django.forms.models import (modelform_factory, modelformset_factory, |
9 | | inlineformset_factory, BaseInlineFormSet, modelform_defines_fields) |
10 | | from django.contrib.contenttypes.models import ContentType |
| 7 | from django.contrib import messages |
11 | 8 | from django.contrib.admin import widgets, helpers |
12 | 9 | from django.contrib.admin.util import (unquote, flatten_fieldsets, get_deleted_objects, |
13 | 10 | model_format_dict, NestedObjects, lookup_needs_distinct) |
14 | 11 | from django.contrib.admin import validation |
15 | 12 | from django.contrib.admin.templatetags.admin_static import static |
16 | 13 | from django.contrib.admin.templatetags.admin_urls import add_preserved_filters |
17 | | from django.contrib import messages |
18 | | from django.views.decorators.csrf import csrf_protect |
| 14 | from django.contrib.auth import get_permission_codename |
| 15 | from django.contrib.contenttypes.models import ContentType |
19 | 16 | from django.core.exceptions import PermissionDenied, ValidationError, FieldError |
20 | 17 | from django.core.paginator import Paginator |
21 | 18 | from django.core.urlresolvers import reverse |
… |
… |
from django.db.models.constants import LOOKUP_SEP
|
24 | 21 | from django.db.models.related import RelatedObject |
25 | 22 | from django.db.models.fields import BLANK_CHOICE_DASH, FieldDoesNotExist |
26 | 23 | from django.db.models.sql.constants import QUERY_TERMS |
| 24 | from django.forms.formsets import all_valid, DELETION_FIELD_NAME |
| 25 | from django.forms.models import (modelform_factory, modelformset_factory, |
| 26 | inlineformset_factory, BaseInlineFormSet, modelform_defines_fields) |
27 | 27 | from django.http import Http404, HttpResponse, HttpResponseRedirect |
28 | 28 | from django.http.response import HttpResponseBase |
29 | 29 | from django.shortcuts import get_object_or_404 |
… |
… |
from django.utils.text import capfirst, get_text_list
|
39 | 39 | from django.utils.translation import ugettext as _ |
40 | 40 | from django.utils.translation import ungettext |
41 | 41 | from django.utils.encoding import force_text |
| 42 | from django.views.decorators.csrf import csrf_protect |
| 43 | |
42 | 44 | |
43 | 45 | IS_POPUP_VAR = '_popup' |
44 | 46 | |
… |
… |
FORMFIELD_FOR_DBFIELD_DEFAULTS = {
|
58 | 60 | 'form_class': forms.SplitDateTimeField, |
59 | 61 | 'widget': widgets.AdminSplitDateTime |
60 | 62 | }, |
61 | | models.DateField: {'widget': widgets.AdminDateWidget}, |
62 | | models.TimeField: {'widget': widgets.AdminTimeWidget}, |
63 | | models.TextField: {'widget': widgets.AdminTextareaWidget}, |
64 | | models.URLField: {'widget': widgets.AdminURLFieldWidget}, |
65 | | models.IntegerField: {'widget': widgets.AdminIntegerFieldWidget}, |
| 63 | models.DateField: {'widget': widgets.AdminDateWidget}, |
| 64 | models.TimeField: {'widget': widgets.AdminTimeWidget}, |
| 65 | models.TextField: {'widget': widgets.AdminTextareaWidget}, |
| 66 | models.URLField: {'widget': widgets.AdminURLFieldWidget}, |
| 67 | models.IntegerField: {'widget': widgets.AdminIntegerFieldWidget}, |
66 | 68 | models.BigIntegerField: {'widget': widgets.AdminBigIntegerFieldWidget}, |
67 | | models.CharField: {'widget': widgets.AdminTextInputWidget}, |
68 | | models.ImageField: {'widget': widgets.AdminFileWidget}, |
69 | | models.FileField: {'widget': widgets.AdminFileWidget}, |
| 69 | models.CharField: {'widget': widgets.AdminTextInputWidget}, |
| 70 | models.ImageField: {'widget': widgets.AdminFileWidget}, |
| 71 | models.FileField: {'widget': widgets.AdminFileWidget}, |
70 | 72 | } |
71 | 73 | |
72 | 74 | csrf_protect_m = method_decorator(csrf_protect) |
… |
… |
class BaseModelAdmin(six.with_metaclass(RenameBaseModelAdminMethods)):
|
352 | 354 | Can be overridden by the user in subclasses. |
353 | 355 | """ |
354 | 356 | opts = self.opts |
355 | | return request.user.has_perm(opts.app_label + '.' + opts.get_add_permission()) |
| 357 | codename = get_permission_codename('add', opts) |
| 358 | return request.user.has_perm("%s.%s" % (opts.app_label, codename)) |
356 | 359 | |
357 | 360 | def has_change_permission(self, request, obj=None): |
358 | 361 | """ |
… |
… |
class BaseModelAdmin(six.with_metaclass(RenameBaseModelAdminMethods)):
|
366 | 369 | request has permission to change *any* object of the given type. |
367 | 370 | """ |
368 | 371 | opts = self.opts |
369 | | return request.user.has_perm(opts.app_label + '.' + opts.get_change_permission()) |
| 372 | codename = get_permission_codename('change', opts) |
| 373 | return request.user.has_perm("%s.%s" % (opts.app_label, codename)) |
370 | 374 | |
371 | 375 | def has_delete_permission(self, request, obj=None): |
372 | 376 | """ |
… |
… |
class BaseModelAdmin(six.with_metaclass(RenameBaseModelAdminMethods)):
|
380 | 384 | request has permission to delete *any* object of the given type. |
381 | 385 | """ |
382 | 386 | opts = self.opts |
383 | | return request.user.has_perm(opts.app_label + '.' + opts.get_delete_permission()) |
| 387 | codename = get_permission_codename('delete', opts) |
| 388 | return request.user.has_perm("%s.%s" % (opts.app_label, codename)) |
| 389 | |
384 | 390 | |
385 | 391 | class ModelAdmin(BaseModelAdmin): |
386 | 392 | "Encapsulates all admin options and functionality for a given model." |
… |
… |
class ModelAdmin(BaseModelAdmin):
|
608 | 614 | """ |
609 | 615 | from django.contrib.admin.models import LogEntry, ADDITION |
610 | 616 | LogEntry.objects.log_action( |
611 | | user_id = request.user.pk, |
612 | | content_type_id = ContentType.objects.get_for_model(object).pk, |
613 | | object_id = object.pk, |
614 | | object_repr = force_text(object), |
615 | | action_flag = ADDITION |
| 617 | user_id=request.user.pk, |
| 618 | content_type_id=ContentType.objects.get_for_model(object).pk, |
| 619 | object_id=object.pk, |
| 620 | object_repr=force_text(object), |
| 621 | action_flag=ADDITION |
616 | 622 | ) |
617 | 623 | |
618 | 624 | def log_change(self, request, object, message): |
… |
… |
class ModelAdmin(BaseModelAdmin):
|
623 | 629 | """ |
624 | 630 | from django.contrib.admin.models import LogEntry, CHANGE |
625 | 631 | LogEntry.objects.log_action( |
626 | | user_id = request.user.pk, |
627 | | content_type_id = ContentType.objects.get_for_model(object).pk, |
628 | | object_id = object.pk, |
629 | | object_repr = force_text(object), |
630 | | action_flag = CHANGE, |
631 | | change_message = message |
| 632 | user_id=request.user.pk, |
| 633 | content_type_id=ContentType.objects.get_for_model(object).pk, |
| 634 | object_id=object.pk, |
| 635 | object_repr=force_text(object), |
| 636 | action_flag=CHANGE, |
| 637 | change_message=message |
632 | 638 | ) |
633 | 639 | |
634 | 640 | def log_deletion(self, request, object, object_repr): |
… |
… |
class ModelAdmin(BaseModelAdmin):
|
640 | 646 | """ |
641 | 647 | from django.contrib.admin.models import LogEntry, DELETION |
642 | 648 | LogEntry.objects.log_action( |
643 | | user_id = request.user.pk, |
644 | | content_type_id = ContentType.objects.get_for_model(self.model).pk, |
645 | | object_id = object.pk, |
646 | | object_repr = object_repr, |
647 | | action_flag = DELETION |
| 649 | user_id=request.user.pk, |
| 650 | content_type_id=ContentType.objects.get_for_model(self.model).pk, |
| 651 | object_id=object.pk, |
| 652 | object_repr=object_repr, |
| 653 | action_flag=DELETION |
648 | 654 | ) |
649 | 655 | |
650 | 656 | def action_checkbox(self, obj): |
… |
… |
class ModelAdmin(BaseModelAdmin):
|
880 | 886 | 'has_add_permission': self.has_add_permission(request), |
881 | 887 | 'has_change_permission': self.has_change_permission(request, obj), |
882 | 888 | 'has_delete_permission': self.has_delete_permission(request, obj), |
883 | | 'has_file_field': True, # FIXME - this should check if form or formsets have a FileField, |
| 889 | 'has_file_field': True, # FIXME - this should check if form or formsets have a FileField, |
884 | 890 | 'has_absolute_url': hasattr(self.model, 'get_absolute_url'), |
885 | 891 | 'form_url': form_url, |
886 | 892 | 'opts': opts, |
… |
… |
class ModelAdmin(BaseModelAdmin):
|
1051 | 1057 | if action_form.is_valid(): |
1052 | 1058 | action = action_form.cleaned_data['action'] |
1053 | 1059 | select_across = action_form.cleaned_data['select_across'] |
1054 | | func, name, description = self.get_actions(request)[action] |
| 1060 | func = self.get_actions(request)[action][0] |
1055 | 1061 | |
1056 | 1062 | # Get the list of selected PKs. If nothing's selected, we can't |
1057 | 1063 | # perform an action on it, so bail. Except we want to perform |
… |
… |
class ModelAdmin(BaseModelAdmin):
|
1282 | 1288 | actions = self.get_actions(request) |
1283 | 1289 | if actions: |
1284 | 1290 | # Add the action checkboxes if there are any actions available. |
1285 | | list_display = ['action_checkbox'] + list(list_display) |
| 1291 | list_display = ['action_checkbox'] + list(list_display) |
1286 | 1292 | |
1287 | 1293 | ChangeList = self.get_changelist(request) |
1288 | 1294 | try: |
… |
… |
class ModelAdmin(BaseModelAdmin):
|
1431 | 1437 | raise PermissionDenied |
1432 | 1438 | |
1433 | 1439 | if obj is None: |
1434 | | raise Http404(_('%(name)s object with primary key %(key)r does not exist.') % {'name': force_text(opts.verbose_name), 'key': escape(object_id)}) |
| 1440 | raise Http404( |
| 1441 | _('%(name)s object with primary key %(key)r does not exist.') % |
| 1442 | {'name': force_text(opts.verbose_name), 'key': escape(object_id)} |
| 1443 | ) |
1435 | 1444 | |
1436 | 1445 | using = router.db_for_write(self.model) |
1437 | 1446 | |
… |
… |
class ModelAdmin(BaseModelAdmin):
|
1440 | 1449 | (deleted_objects, perms_needed, protected) = get_deleted_objects( |
1441 | 1450 | [obj], opts, request.user, self.admin_site, using) |
1442 | 1451 | |
1443 | | if request.POST: # The user has already confirmed the deletion. |
| 1452 | if request.POST: # The user has already confirmed the deletion. |
1444 | 1453 | if perms_needed: |
1445 | 1454 | raise PermissionDenied |
1446 | 1455 | obj_display = force_text(obj) |
… |
… |
class ModelAdmin(BaseModelAdmin):
|
1458 | 1467 | (opts.app_label, opts.model_name), |
1459 | 1468 | current_app=self.admin_site.name) |
1460 | 1469 | preserved_filters = self.get_preserved_filters(request) |
1461 | | post_url = add_preserved_filters({'preserved_filters': preserved_filters, 'opts': opts}, post_url) |
| 1470 | post_url = add_preserved_filters( |
| 1471 | {'preserved_filters': preserved_filters, 'opts': opts}, post_url |
| 1472 | ) |
1462 | 1473 | else: |
1463 | 1474 | post_url = reverse('admin:index', |
1464 | 1475 | current_app=self.admin_site.name) |
… |
… |
class ModelAdmin(BaseModelAdmin):
|
1524 | 1535 | "admin/object_history.html" |
1525 | 1536 | ], context, current_app=self.admin_site.name) |
1526 | 1537 | |
| 1538 | |
1527 | 1539 | class InlineModelAdmin(BaseModelAdmin): |
1528 | 1540 | """ |
1529 | 1541 | Options for inline editing of ``model`` instances. |
… |
… |
class InlineModelAdmin(BaseModelAdmin):
|
1667 | 1679 | # to have the change permission for the related model in order to |
1668 | 1680 | # be able to do anything with the intermediate model. |
1669 | 1681 | return self.has_change_permission(request) |
1670 | | return request.user.has_perm( |
1671 | | self.opts.app_label + '.' + self.opts.get_add_permission()) |
| 1682 | return super(InlineModelAdmin, self).has_add_permission(request) |
1672 | 1683 | |
1673 | 1684 | def has_change_permission(self, request, obj=None): |
1674 | 1685 | opts = self.opts |
… |
… |
class InlineModelAdmin(BaseModelAdmin):
|
1679 | 1690 | if field.rel and field.rel.to != self.parent_model: |
1680 | 1691 | opts = field.rel.to._meta |
1681 | 1692 | break |
1682 | | return request.user.has_perm( |
1683 | | opts.app_label + '.' + opts.get_change_permission()) |
| 1693 | codename = get_permission_codename('change', opts) |
| 1694 | return request.user.has_perm("%s.%s" % (opts.app_label, codename)) |
1684 | 1695 | |
1685 | 1696 | def has_delete_permission(self, request, obj=None): |
1686 | 1697 | if self.opts.auto_created: |
… |
… |
class InlineModelAdmin(BaseModelAdmin):
|
1689 | 1700 | # to have the change permission for the related model in order to |
1690 | 1701 | # be able to do anything with the intermediate model. |
1691 | 1702 | return self.has_change_permission(request, obj) |
1692 | | return request.user.has_perm( |
1693 | | self.opts.app_label + '.' + self.opts.get_delete_permission()) |
| 1703 | return super(InlineModelAdmin, self).has_delete_permission(request, obj) |
1694 | 1704 | |
1695 | 1705 | |
1696 | 1706 | class StackedInline(InlineModelAdmin): |
diff --git a/django/contrib/auth/__init__.py b/django/contrib/auth/__init__.py
index 029193d..2f620a3 100644
a
|
b
|
def logout(request):
|
108 | 108 | |
109 | 109 | |
110 | 110 | def get_user_model(): |
111 | | "Return the User model that is active in this project" |
| 111 | """ |
| 112 | Returns the User model that is active in this project. |
| 113 | """ |
112 | 114 | from django.db.models import get_model |
113 | 115 | |
114 | 116 | try: |
… |
… |
def get_user_model():
|
122 | 124 | |
123 | 125 | |
124 | 126 | def get_user(request): |
| 127 | """ |
| 128 | Returns the user model instance associated with the given request session. |
| 129 | If no user is retrieved an instance of `AnonymousUser` is returned. |
| 130 | """ |
125 | 131 | from .models import AnonymousUser |
126 | 132 | try: |
127 | 133 | user_id = request.session[SESSION_KEY] |
… |
… |
def get_user(request):
|
132 | 138 | except (KeyError, AssertionError): |
133 | 139 | user = AnonymousUser() |
134 | 140 | return user |
| 141 | |
| 142 | |
| 143 | def get_permission_codename(action, opts): |
| 144 | """ |
| 145 | Returns the codename of the permission for the specified action. |
| 146 | """ |
| 147 | return '%s_%s' % (action, opts.model_name) |
diff --git a/django/contrib/auth/management/__init__.py b/django/contrib/auth/management/__init__.py
index 5c1bfbc..1f33846 100644
a
|
b
|
from __future__ import unicode_literals
|
6 | 6 | import getpass |
7 | 7 | import unicodedata |
8 | 8 | |
9 | | from django.contrib.auth import models as auth_app, get_user_model |
| 9 | from django.contrib.auth import (models as auth_app, get_permission_codename, |
| 10 | get_user_model) |
10 | 11 | from django.core import exceptions |
11 | 12 | from django.core.management.base import CommandError |
12 | 13 | from django.db import DEFAULT_DB_ALIAS, router |
… |
… |
from django.utils import six
|
16 | 17 | from django.utils.six.moves import input |
17 | 18 | |
18 | 19 | |
19 | | def _get_permission_codename(action, opts): |
20 | | return '%s_%s' % (action, opts.model_name) |
21 | | |
22 | | |
23 | 20 | def _get_all_permissions(opts, ctype): |
24 | 21 | """ |
25 | 22 | Returns (codename, name) for all permissions in the given opts. |
… |
… |
def _get_all_permissions(opts, ctype):
|
29 | 26 | _check_permission_clashing(custom, builtin, ctype) |
30 | 27 | return builtin + custom |
31 | 28 | |
| 29 | |
32 | 30 | def _get_builtin_permissions(opts): |
33 | 31 | """ |
34 | 32 | Returns (codename, name) for all autogenerated permissions. |
35 | 33 | """ |
36 | 34 | perms = [] |
37 | 35 | for action in ('add', 'change', 'delete'): |
38 | | perms.append((_get_permission_codename(action, opts), |
| 36 | perms.append((get_permission_codename(action, opts), |
39 | 37 | 'Can %s %s' % (action, opts.verbose_name_raw))) |
40 | 38 | return perms |
41 | 39 | |
| 40 | |
42 | 41 | def _check_permission_clashing(custom, builtin, ctype): |
43 | 42 | """ |
44 | 43 | Check that permissions for a model do not clash. Raises CommandError if |
… |
… |
def _check_permission_clashing(custom, builtin, ctype):
|
58 | 57 | (codename, ctype.app_label, ctype.model_class().__name__)) |
59 | 58 | pool.add(codename) |
60 | 59 | |
| 60 | |
61 | 61 | def create_permissions(app, created_models, verbosity, db=DEFAULT_DB_ALIAS, **kwargs): |
62 | 62 | try: |
63 | 63 | get_model('auth', 'Permission') |
diff --git a/django/db/models/options.py b/django/db/models/options.py
index b8a7902..ad25de4 100644
a
|
b
|
class Options(object):
|
414 | 414 | return cache |
415 | 415 | |
416 | 416 | def get_add_permission(self): |
| 417 | """ |
| 418 | This method has been deprecated in favor of |
| 419 | `django.contrib.auth.get_permission_codename`. refs #20642 |
| 420 | """ |
| 421 | warnings.warn( |
| 422 | "`Options.get_add_permission` has been deprecated in favor " |
| 423 | "of `django.contrib.auth.get_permission_codename`.", |
| 424 | PendingDeprecationWarning, stacklevel=2) |
417 | 425 | return 'add_%s' % self.model_name |
418 | 426 | |
419 | 427 | def get_change_permission(self): |
| 428 | """ |
| 429 | This method has been deprecated in favor of |
| 430 | `django.contrib.auth.get_permission_codename`. refs #20642 |
| 431 | """ |
| 432 | warnings.warn( |
| 433 | "`Options.get_change_permission` has been deprecated in favor " |
| 434 | "of `django.contrib.auth.get_permission_codename`.", |
| 435 | PendingDeprecationWarning, stacklevel=2) |
420 | 436 | return 'change_%s' % self.model_name |
421 | 437 | |
422 | 438 | def get_delete_permission(self): |
| 439 | """ |
| 440 | This method has been deprecated in favor of |
| 441 | `django.contrib.auth.get_permission_codename`. refs #20642 |
| 442 | """ |
| 443 | warnings.warn( |
| 444 | "`Options.get_delete_permission` has been deprecated in favor " |
| 445 | "of `django.contrib.auth.get_permission_codename`.", |
| 446 | PendingDeprecationWarning, stacklevel=2) |
423 | 447 | return 'delete_%s' % self.model_name |
424 | 448 | |
425 | 449 | def get_all_related_objects(self, local_only=False, include_hidden=False, |
diff --git a/docs/releases/1.6.txt b/docs/releases/1.6.txt
index 95bfedc..5a611af 100644
a
|
b
|
on a widget, you should now define this method on the form field itself.
|
851 | 851 | ``Model._meta.module_name`` was renamed to ``model_name``. Despite being a |
852 | 852 | private API, it will go through a regular deprecation path. |
853 | 853 | |
| 854 | ``get_(add|change|delete)_permission`` model _meta methods |
| 855 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 856 | |
| 857 | ``Model._meta.get_(add|change|delete)_permission`` methods were deprecated. |
| 858 | Even if they were not part of the public API they'll also go through |
| 859 | a regular deprecation path. |
| 860 | |
854 | 861 | ``get_query_set`` and similar methods renamed to ``get_queryset`` |
855 | 862 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
856 | 863 | |