From 9c711ee3a6a638add26d19dad70447c981371598 Mon Sep 17 00:00:00 2001 From: Tim Graham Date: Fri, 9 Aug 2013 09:12:15 -0400 Subject: [PATCH 1/2] Fixed test failures on Python 3 - refs #12288 --- django/conf/__init__.py | 6 ++++-- tests/settings_tests/tests.py | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/django/conf/__init__.py b/django/conf/__init__.py index e628af748c..199c34fb55 100644 --- a/django/conf/__init__.py +++ b/django/conf/__init__.py @@ -107,8 +107,10 @@ class BaseSettings(object): elif name == "ALLOWED_INCLUDE_ROOTS" and isinstance(value, six.string_types): raise ValueError("The ALLOWED_INCLUDE_ROOTS setting must be set " "to a tuple, not a string.") - elif name == "INSTALLED_APPS" and len(value) != len(set(value)): - raise ImproperlyConfigured("The INSTALLED_APPS setting must contain unique values.") + elif name == "INSTALLED_APPS": + value = list(value) # force evaluation of generators on Python 3 + if len(value) != len(set(value)): + raise ImproperlyConfigured("The INSTALLED_APPS setting must contain unique values.") object.__setattr__(self, name, value) diff --git a/tests/settings_tests/tests.py b/tests/settings_tests/tests.py index 4031d09a58..a9503358a2 100644 --- a/tests/settings_tests/tests.py +++ b/tests/settings_tests/tests.py @@ -226,7 +226,7 @@ class TestComplexSettingOverride(TestCase): self.assertEqual('Overriding setting TEST_WARN can lead to unexpected behaviour.', str(w[-1].message)) -class UniqueSettngsTests(TestCase): +class UniqueSettingsTests(TestCase): """ Tests for the INSTALLED_APPS setting. """ From ddae74b64ccb1173e3f2f06d54dd45643130f9e0 Mon Sep 17 00:00:00 2001 From: Tim Graham Date: Thu, 1 Aug 2013 11:31:34 -0400 Subject: [PATCH 2/2] Fixed #9057 -- Added default_permissions model meta option. Thanks hvendelbo for the suggestion and koenb for the draft patch. --- django/contrib/auth/management/__init__.py | 3 ++- django/contrib/auth/tests/test_management.py | 24 +++++++++++++++++++- django/db/models/options.py | 3 ++- docs/ref/models/options.txt | 13 +++++++++++ docs/releases/1.7.txt | 4 ++++ 5 files changed, 44 insertions(+), 3 deletions(-) diff --git a/django/contrib/auth/management/__init__.py b/django/contrib/auth/management/__init__.py index 1f338469f8..8335f35fac 100644 --- a/django/contrib/auth/management/__init__.py +++ b/django/contrib/auth/management/__init__.py @@ -30,9 +30,10 @@ def _get_all_permissions(opts, ctype): def _get_builtin_permissions(opts): """ Returns (codename, name) for all autogenerated permissions. + By default, this is ('add', 'change', 'delete') """ perms = [] - for action in ('add', 'change', 'delete'): + for action in opts.default_permissions: perms.append((get_permission_codename(action, opts), 'Can %s %s' % (action, opts.verbose_name_raw))) return perms diff --git a/django/contrib/auth/tests/test_management.py b/django/contrib/auth/tests/test_management.py index 6c3718465d..3711f52dea 100644 --- a/django/contrib/auth/tests/test_management.py +++ b/django/contrib/auth/tests/test_management.py @@ -196,13 +196,15 @@ class CustomUserModelValidationTestCase(TestCase): self.assertIn("The USERNAME_FIELD must be unique. Add unique=True to the field parameters.", new_io.getvalue()) -class PermissionDuplicationTestCase(TestCase): +class PermissionTestCase(TestCase): def setUp(self): self._original_permissions = models.Permission._meta.permissions[:] + self._original_default_permissions = models.Permission._meta.default_permissions def tearDown(self): models.Permission._meta.permissions = self._original_permissions + models.Permission._meta.default_permissions = self._original_default_permissions ContentType.objects.clear_cache() def test_duplicated_permissions(self): @@ -235,3 +237,23 @@ class PermissionDuplicationTestCase(TestCase): ('other_one', 'Some other permission'), ] create_permissions(models, [], verbosity=0) + + def test_default_permissions(self): + models.Permission._meta.permissions = [ + ('my_custom_permission', 'Some permission'), + ] + create_permissions(models, [], verbosity=0) + + # add/change/delete permission by default + custom permission + self.assertEqual(models.Permission.objects.filter(content_type= + ContentType.objects.get_by_natural_key('auth', 'permission') + ).count(), 4) + + models.Permission.objects.all().delete() + models.Permission._meta.default_permissions = [] + create_permissions(models, [], verbosity=0) + + # custom permission only since default permissions is empty + self.assertEqual(models.Permission.objects.filter(content_type= + ContentType.objects.get_by_natural_key('auth', 'permission') + ).count(), 1) diff --git a/django/db/models/options.py b/django/db/models/options.py index 95cefec1bf..5f269f83b9 100644 --- a/django/db/models/options.py +++ b/django/db/models/options.py @@ -22,7 +22,7 @@ DEFAULT_NAMES = ('verbose_name', 'verbose_name_plural', 'db_table', 'ordering', 'unique_together', 'permissions', 'get_latest_by', 'order_with_respect_to', 'app_label', 'db_tablespace', 'abstract', 'managed', 'proxy', 'swappable', 'auto_created', - 'index_together') + 'index_together', 'default_permissions') @python_2_unicode_compatible @@ -36,6 +36,7 @@ class Options(object): self.ordering = [] self.unique_together = [] self.index_together = [] + self.default_permissions = ('add', 'change', 'delete') self.permissions = [] self.object_name, self.app_label = None, app_label self.get_latest_by = None diff --git a/docs/ref/models/options.txt b/docs/ref/models/options.txt index 7d751a53fe..3e8f64c37a 100644 --- a/docs/ref/models/options.txt +++ b/docs/ref/models/options.txt @@ -235,6 +235,19 @@ Django quotes column and table names behind the scenes. This is a list or tuple of 2-tuples in the format ``(permission_code, human_readable_permission_name)``. +``default_permissions`` +------------------------------ + +.. attribute:: Options.default_permissions + + .. versionadded:: 1.7 + + Defaults to ``('add', 'change', 'delete')``. You may customize this list, + for example, by setting this to an empty list if your app doesn't require + any of the default permissions. It must be specified on the model before + the model is created by :djadmin:`syncdb` in order to prevent any omitted + permissions from being created. + ``proxy`` --------- diff --git a/docs/releases/1.7.txt b/docs/releases/1.7.txt index 26a0beeece..6c28d6e1d0 100644 --- a/docs/releases/1.7.txt +++ b/docs/releases/1.7.txt @@ -131,6 +131,10 @@ Minor features return ``self.cleaned_data``. If it does return a changed dictionary then that will still be used. +* The new :attr:`~django.db.models.Options.default_permissions` model + ``Meta`` option allows you to customize (or disable) creation of the default + add, change, and delete permissions. + Backwards incompatible changes in 1.7 =====================================