From 4ad1eb1c14b629cf5bcfd253ed40e875f1bddd47 Mon Sep 17 00:00:00 2001 From: Honza Kral Date: Sat, 23 Feb 2013 16:10:32 +0100 Subject: Fixed #12674 -- provide a way to override admin validation Moved admin validation code to classes and have those be class attributes to the ModelAdmin classes. --- tests/modeladmin/tests.py | 239 +++++++++++++++++----------------------------- 1 file changed, 90 insertions(+), 149 deletions(-) (limited to 'tests/modeladmin') diff --git a/tests/modeladmin/tests.py b/tests/modeladmin/tests.py index 0d933bc1f9..f89f1c20ec 100644 --- a/tests/modeladmin/tests.py +++ b/tests/modeladmin/tests.py @@ -7,7 +7,6 @@ from django.conf import settings from django.contrib.admin.options import (ModelAdmin, TabularInline, HORIZONTAL, VERTICAL) from django.contrib.admin.sites import AdminSite -from django.contrib.admin.validation import validate from django.contrib.admin.widgets import AdminDateWidget, AdminRadioSelect from django.contrib.admin import (SimpleListFilter, BooleanFieldListFilter) @@ -523,8 +522,7 @@ class ValidationTests(unittest.TestCase): six.assertRaisesRegex(self, ImproperlyConfigured, "'ValidationTestModelAdmin.raw_id_fields' must be a list or tuple.", - validate, - ValidationTestModelAdmin, + ValidationTestModelAdmin.validate, ValidationTestModel, ) @@ -534,8 +532,7 @@ class ValidationTests(unittest.TestCase): six.assertRaisesRegex(self, ImproperlyConfigured, "'ValidationTestModelAdmin.raw_id_fields' refers to field 'non_existent_field' that is missing from model 'modeladmin.ValidationTestModel'.", - validate, - ValidationTestModelAdmin, + ValidationTestModelAdmin.validate, ValidationTestModel, ) @@ -545,15 +542,14 @@ class ValidationTests(unittest.TestCase): six.assertRaisesRegex(self, ImproperlyConfigured, "'ValidationTestModelAdmin.raw_id_fields\[0\]', 'name' must be either a ForeignKey or ManyToManyField.", - validate, - ValidationTestModelAdmin, + ValidationTestModelAdmin.validate, ValidationTestModel, ) class ValidationTestModelAdmin(ModelAdmin): raw_id_fields = ('users',) - validate(ValidationTestModelAdmin, ValidationTestModel) + ValidationTestModelAdmin.validate(ValidationTestModel) def test_fieldsets_validation(self): @@ -563,8 +559,7 @@ class ValidationTests(unittest.TestCase): six.assertRaisesRegex(self, ImproperlyConfigured, "'ValidationTestModelAdmin.fieldsets' must be a list or tuple.", - validate, - ValidationTestModelAdmin, + ValidationTestModelAdmin.validate, ValidationTestModel, ) @@ -574,8 +569,7 @@ class ValidationTests(unittest.TestCase): six.assertRaisesRegex(self, ImproperlyConfigured, "'ValidationTestModelAdmin.fieldsets\[0\]' must be a list or tuple.", - validate, - ValidationTestModelAdmin, + ValidationTestModelAdmin.validate, ValidationTestModel, ) @@ -585,8 +579,7 @@ class ValidationTests(unittest.TestCase): six.assertRaisesRegex(self, ImproperlyConfigured, "'ValidationTestModelAdmin.fieldsets\[0\]' does not have exactly two elements.", - validate, - ValidationTestModelAdmin, + ValidationTestModelAdmin.validate, ValidationTestModel, ) @@ -596,8 +589,7 @@ class ValidationTests(unittest.TestCase): six.assertRaisesRegex(self, ImproperlyConfigured, "'ValidationTestModelAdmin.fieldsets\[0\]\[1\]' must be a dictionary.", - validate, - ValidationTestModelAdmin, + ValidationTestModelAdmin.validate, ValidationTestModel, ) @@ -607,15 +599,14 @@ class ValidationTests(unittest.TestCase): six.assertRaisesRegex(self, ImproperlyConfigured, "'fields' key is required in ValidationTestModelAdmin.fieldsets\[0\]\[1\] field options dict.", - validate, - ValidationTestModelAdmin, + ValidationTestModelAdmin.validate, ValidationTestModel, ) class ValidationTestModelAdmin(ModelAdmin): fieldsets = (("General", {"fields": ("name",)}),) - validate(ValidationTestModelAdmin, ValidationTestModel) + ValidationTestModelAdmin.validate(ValidationTestModel) class ValidationTestModelAdmin(ModelAdmin): fieldsets = (("General", {"fields": ("name",)}),) @@ -624,8 +615,7 @@ class ValidationTests(unittest.TestCase): six.assertRaisesRegex(self, ImproperlyConfigured, "Both fieldsets and fields are specified in ValidationTestModelAdmin.", - validate, - ValidationTestModelAdmin, + ValidationTestModelAdmin.validate, ValidationTestModel, ) @@ -635,8 +625,7 @@ class ValidationTests(unittest.TestCase): six.assertRaisesRegex(self, ImproperlyConfigured, "There are duplicate field\(s\) in ValidationTestModelAdmin.fieldsets", - validate, - ValidationTestModelAdmin, + ValidationTestModelAdmin.validate, ValidationTestModel, ) @@ -646,8 +635,7 @@ class ValidationTests(unittest.TestCase): six.assertRaisesRegex(self, ImproperlyConfigured, "There are duplicate field\(s\) in ValidationTestModelAdmin.fields", - validate, - ValidationTestModelAdmin, + ValidationTestModelAdmin.validate, ValidationTestModel, ) @@ -662,8 +650,7 @@ class ValidationTests(unittest.TestCase): six.assertRaisesRegex(self, ImproperlyConfigured, "ValidationTestModelAdmin.form does not inherit from BaseModelForm.", - validate, - ValidationTestModelAdmin, + ValidationTestModelAdmin.validate, ValidationTestModel, ) @@ -676,7 +663,7 @@ class ValidationTests(unittest.TestCase): }), ) - validate(BandAdmin, Band) + BandAdmin.validate(Band) class AdminBandForm(forms.ModelForm): delete = forms.BooleanField() @@ -690,7 +677,7 @@ class ValidationTests(unittest.TestCase): }), ) - validate(BandAdmin, Band) + BandAdmin.validate(Band) def test_filter_vertical_validation(self): @@ -700,8 +687,7 @@ class ValidationTests(unittest.TestCase): six.assertRaisesRegex(self, ImproperlyConfigured, "'ValidationTestModelAdmin.filter_vertical' must be a list or tuple.", - validate, - ValidationTestModelAdmin, + ValidationTestModelAdmin.validate, ValidationTestModel, ) @@ -711,8 +697,7 @@ class ValidationTests(unittest.TestCase): six.assertRaisesRegex(self, ImproperlyConfigured, "'ValidationTestModelAdmin.filter_vertical' refers to field 'non_existent_field' that is missing from model 'modeladmin.ValidationTestModel'.", - validate, - ValidationTestModelAdmin, + ValidationTestModelAdmin.validate, ValidationTestModel, ) @@ -722,15 +707,14 @@ class ValidationTests(unittest.TestCase): six.assertRaisesRegex(self, ImproperlyConfigured, "'ValidationTestModelAdmin.filter_vertical\[0\]' must be a ManyToManyField.", - validate, - ValidationTestModelAdmin, + ValidationTestModelAdmin.validate, ValidationTestModel, ) class ValidationTestModelAdmin(ModelAdmin): filter_vertical = ("users",) - validate(ValidationTestModelAdmin, ValidationTestModel) + ValidationTestModelAdmin.validate(ValidationTestModel) def test_filter_horizontal_validation(self): @@ -740,8 +724,7 @@ class ValidationTests(unittest.TestCase): six.assertRaisesRegex(self, ImproperlyConfigured, "'ValidationTestModelAdmin.filter_horizontal' must be a list or tuple.", - validate, - ValidationTestModelAdmin, + ValidationTestModelAdmin.validate, ValidationTestModel, ) @@ -751,8 +734,7 @@ class ValidationTests(unittest.TestCase): six.assertRaisesRegex(self, ImproperlyConfigured, "'ValidationTestModelAdmin.filter_horizontal' refers to field 'non_existent_field' that is missing from model 'modeladmin.ValidationTestModel'.", - validate, - ValidationTestModelAdmin, + ValidationTestModelAdmin.validate, ValidationTestModel, ) @@ -762,15 +744,14 @@ class ValidationTests(unittest.TestCase): six.assertRaisesRegex(self, ImproperlyConfigured, "'ValidationTestModelAdmin.filter_horizontal\[0\]' must be a ManyToManyField.", - validate, - ValidationTestModelAdmin, + ValidationTestModelAdmin.validate, ValidationTestModel, ) class ValidationTestModelAdmin(ModelAdmin): filter_horizontal = ("users",) - validate(ValidationTestModelAdmin, ValidationTestModel) + ValidationTestModelAdmin.validate(ValidationTestModel) def test_radio_fields_validation(self): @@ -780,8 +761,7 @@ class ValidationTests(unittest.TestCase): six.assertRaisesRegex(self, ImproperlyConfigured, "'ValidationTestModelAdmin.radio_fields' must be a dictionary.", - validate, - ValidationTestModelAdmin, + ValidationTestModelAdmin.validate, ValidationTestModel, ) @@ -791,8 +771,7 @@ class ValidationTests(unittest.TestCase): six.assertRaisesRegex(self, ImproperlyConfigured, "'ValidationTestModelAdmin.radio_fields' refers to field 'non_existent_field' that is missing from model 'modeladmin.ValidationTestModel'.", - validate, - ValidationTestModelAdmin, + ValidationTestModelAdmin.validate, ValidationTestModel, ) @@ -802,8 +781,7 @@ class ValidationTests(unittest.TestCase): six.assertRaisesRegex(self, ImproperlyConfigured, "'ValidationTestModelAdmin.radio_fields\['name'\]' is neither an instance of ForeignKey nor does have choices set.", - validate, - ValidationTestModelAdmin, + ValidationTestModelAdmin.validate, ValidationTestModel, ) @@ -813,15 +791,14 @@ class ValidationTests(unittest.TestCase): six.assertRaisesRegex(self, ImproperlyConfigured, "'ValidationTestModelAdmin.radio_fields\['state'\]' is neither admin.HORIZONTAL nor admin.VERTICAL.", - validate, - ValidationTestModelAdmin, + ValidationTestModelAdmin.validate, ValidationTestModel, ) class ValidationTestModelAdmin(ModelAdmin): radio_fields = {"state": VERTICAL} - validate(ValidationTestModelAdmin, ValidationTestModel) + ValidationTestModelAdmin.validate(ValidationTestModel) def test_prepopulated_fields_validation(self): @@ -831,8 +808,7 @@ class ValidationTests(unittest.TestCase): six.assertRaisesRegex(self, ImproperlyConfigured, "'ValidationTestModelAdmin.prepopulated_fields' must be a dictionary.", - validate, - ValidationTestModelAdmin, + ValidationTestModelAdmin.validate, ValidationTestModel, ) @@ -842,8 +818,7 @@ class ValidationTests(unittest.TestCase): six.assertRaisesRegex(self, ImproperlyConfigured, "'ValidationTestModelAdmin.prepopulated_fields' refers to field 'non_existent_field' that is missing from model 'modeladmin.ValidationTestModel'.", - validate, - ValidationTestModelAdmin, + ValidationTestModelAdmin.validate, ValidationTestModel, ) @@ -853,8 +828,7 @@ class ValidationTests(unittest.TestCase): six.assertRaisesRegex(self, ImproperlyConfigured, "'ValidationTestModelAdmin.prepopulated_fields\['slug'\]\[0\]' refers to field 'non_existent_field' that is missing from model 'modeladmin.ValidationTestModel'.", - validate, - ValidationTestModelAdmin, + ValidationTestModelAdmin.validate, ValidationTestModel, ) @@ -864,15 +838,14 @@ class ValidationTests(unittest.TestCase): six.assertRaisesRegex(self, ImproperlyConfigured, "'ValidationTestModelAdmin.prepopulated_fields\['users'\]' is either a DateTimeField, ForeignKey or ManyToManyField. This isn't allowed.", - validate, - ValidationTestModelAdmin, + ValidationTestModelAdmin.validate, ValidationTestModel, ) class ValidationTestModelAdmin(ModelAdmin): prepopulated_fields = {"slug": ("name",)} - validate(ValidationTestModelAdmin, ValidationTestModel) + ValidationTestModelAdmin.validate(ValidationTestModel) def test_list_display_validation(self): @@ -882,8 +855,7 @@ class ValidationTests(unittest.TestCase): six.assertRaisesRegex(self, ImproperlyConfigured, "'ValidationTestModelAdmin.list_display' must be a list or tuple.", - validate, - ValidationTestModelAdmin, + ValidationTestModelAdmin.validate, ValidationTestModel, ) @@ -893,8 +865,7 @@ class ValidationTests(unittest.TestCase): six.assertRaisesRegex(self, ImproperlyConfigured, str_prefix("ValidationTestModelAdmin.list_display\[0\], %(_)s'non_existent_field' is not a callable or an attribute of 'ValidationTestModelAdmin' or found in the model 'ValidationTestModel'."), - validate, - ValidationTestModelAdmin, + ValidationTestModelAdmin.validate, ValidationTestModel, ) @@ -904,8 +875,7 @@ class ValidationTests(unittest.TestCase): six.assertRaisesRegex(self, ImproperlyConfigured, "'ValidationTestModelAdmin.list_display\[0\]', 'users' is a ManyToManyField which is not supported.", - validate, - ValidationTestModelAdmin, + ValidationTestModelAdmin.validate, ValidationTestModel, ) @@ -917,7 +887,7 @@ class ValidationTests(unittest.TestCase): pass list_display = ('name', 'decade_published_in', 'a_method', a_callable) - validate(ValidationTestModelAdmin, ValidationTestModel) + ValidationTestModelAdmin.validate(ValidationTestModel) def test_list_display_links_validation(self): @@ -927,8 +897,7 @@ class ValidationTests(unittest.TestCase): six.assertRaisesRegex(self, ImproperlyConfigured, "'ValidationTestModelAdmin.list_display_links' must be a list or tuple.", - validate, - ValidationTestModelAdmin, + ValidationTestModelAdmin.validate, ValidationTestModel, ) @@ -938,8 +907,7 @@ class ValidationTests(unittest.TestCase): six.assertRaisesRegex(self, ImproperlyConfigured, "'ValidationTestModelAdmin.list_display_links\[0\]' refers to 'non_existent_field' which is not defined in 'list_display'.", - validate, - ValidationTestModelAdmin, + ValidationTestModelAdmin.validate, ValidationTestModel, ) @@ -949,8 +917,7 @@ class ValidationTests(unittest.TestCase): six.assertRaisesRegex(self, ImproperlyConfigured, "'ValidationTestModelAdmin.list_display_links\[0\]' refers to 'name' which is not defined in 'list_display'.", - validate, - ValidationTestModelAdmin, + ValidationTestModelAdmin.validate, ValidationTestModel, ) @@ -963,7 +930,7 @@ class ValidationTests(unittest.TestCase): list_display = ('name', 'decade_published_in', 'a_method', a_callable) list_display_links = ('name', 'decade_published_in', 'a_method', a_callable) - validate(ValidationTestModelAdmin, ValidationTestModel) + ValidationTestModelAdmin.validate(ValidationTestModel) def test_list_filter_validation(self): @@ -973,8 +940,7 @@ class ValidationTests(unittest.TestCase): six.assertRaisesRegex(self, ImproperlyConfigured, "'ValidationTestModelAdmin.list_filter' must be a list or tuple.", - validate, - ValidationTestModelAdmin, + ValidationTestModelAdmin.validate, ValidationTestModel, ) @@ -984,8 +950,7 @@ class ValidationTests(unittest.TestCase): six.assertRaisesRegex(self, ImproperlyConfigured, "'ValidationTestModelAdmin.list_filter\[0\]' refers to 'non_existent_field' which does not refer to a Field.", - validate, - ValidationTestModelAdmin, + ValidationTestModelAdmin.validate, ValidationTestModel, ) @@ -998,8 +963,7 @@ class ValidationTests(unittest.TestCase): six.assertRaisesRegex(self, ImproperlyConfigured, "'ValidationTestModelAdmin.list_filter\[0\]' is 'RandomClass' which is not a descendant of ListFilter.", - validate, - ValidationTestModelAdmin, + ValidationTestModelAdmin.validate, ValidationTestModel, ) @@ -1009,8 +973,7 @@ class ValidationTests(unittest.TestCase): six.assertRaisesRegex(self, ImproperlyConfigured, "'ValidationTestModelAdmin.list_filter\[0\]\[1\]' is 'RandomClass' which is not of type FieldListFilter.", - validate, - ValidationTestModelAdmin, + ValidationTestModelAdmin.validate, ValidationTestModel, ) @@ -1028,8 +991,7 @@ class ValidationTests(unittest.TestCase): six.assertRaisesRegex(self, ImproperlyConfigured, "'ValidationTestModelAdmin.list_filter\[0\]\[1\]' is 'AwesomeFilter' which is not of type FieldListFilter.", - validate, - ValidationTestModelAdmin, + ValidationTestModelAdmin.validate, ValidationTestModel, ) @@ -1039,8 +1001,7 @@ class ValidationTests(unittest.TestCase): six.assertRaisesRegex(self, ImproperlyConfigured, "'ValidationTestModelAdmin.list_filter\[0\]' is 'BooleanFieldListFilter' which is of type FieldListFilter but is not associated with a field name.", - validate, - ValidationTestModelAdmin, + ValidationTestModelAdmin.validate, ValidationTestModel, ) @@ -1049,7 +1010,7 @@ class ValidationTests(unittest.TestCase): class ValidationTestModelAdmin(ModelAdmin): list_filter = ('is_active', AwesomeFilter, ('is_active', BooleanFieldListFilter), 'no') - validate(ValidationTestModelAdmin, ValidationTestModel) + ValidationTestModelAdmin.validate(ValidationTestModel) def test_list_per_page_validation(self): @@ -1058,16 +1019,15 @@ class ValidationTests(unittest.TestCase): six.assertRaisesRegex(self, ImproperlyConfigured, - "'ValidationTestModelAdmin.list_per_page' should be a integer.", - validate, - ValidationTestModelAdmin, + "'ValidationTestModelAdmin.list_per_page' should be a int.", + ValidationTestModelAdmin.validate, ValidationTestModel, ) class ValidationTestModelAdmin(ModelAdmin): list_per_page = 100 - validate(ValidationTestModelAdmin, ValidationTestModel) + ValidationTestModelAdmin.validate(ValidationTestModel) def test_max_show_all_allowed_validation(self): @@ -1076,16 +1036,15 @@ class ValidationTests(unittest.TestCase): six.assertRaisesRegex(self, ImproperlyConfigured, - "'ValidationTestModelAdmin.list_max_show_all' should be an integer.", - validate, - ValidationTestModelAdmin, + "'ValidationTestModelAdmin.list_max_show_all' should be a int.", + ValidationTestModelAdmin.validate, ValidationTestModel, ) class ValidationTestModelAdmin(ModelAdmin): list_max_show_all = 200 - validate(ValidationTestModelAdmin, ValidationTestModel) + ValidationTestModelAdmin.validate(ValidationTestModel) def test_search_fields_validation(self): @@ -1095,8 +1054,7 @@ class ValidationTests(unittest.TestCase): six.assertRaisesRegex(self, ImproperlyConfigured, "'ValidationTestModelAdmin.search_fields' must be a list or tuple.", - validate, - ValidationTestModelAdmin, + ValidationTestModelAdmin.validate, ValidationTestModel, ) @@ -1108,8 +1066,7 @@ class ValidationTests(unittest.TestCase): six.assertRaisesRegex(self, ImproperlyConfigured, "'ValidationTestModelAdmin.date_hierarchy' refers to field 'non_existent_field' that is missing from model 'modeladmin.ValidationTestModel'.", - validate, - ValidationTestModelAdmin, + ValidationTestModelAdmin.validate, ValidationTestModel, ) @@ -1119,15 +1076,14 @@ class ValidationTests(unittest.TestCase): six.assertRaisesRegex(self, ImproperlyConfigured, "'ValidationTestModelAdmin.date_hierarchy is neither an instance of DateField nor DateTimeField.", - validate, - ValidationTestModelAdmin, + ValidationTestModelAdmin.validate, ValidationTestModel, ) class ValidationTestModelAdmin(ModelAdmin): date_hierarchy = 'pub_date' - validate(ValidationTestModelAdmin, ValidationTestModel) + ValidationTestModelAdmin.validate(ValidationTestModel) def test_ordering_validation(self): @@ -1137,8 +1093,7 @@ class ValidationTests(unittest.TestCase): six.assertRaisesRegex(self, ImproperlyConfigured, "'ValidationTestModelAdmin.ordering' must be a list or tuple.", - validate, - ValidationTestModelAdmin, + ValidationTestModelAdmin.validate, ValidationTestModel, ) @@ -1148,8 +1103,7 @@ class ValidationTests(unittest.TestCase): six.assertRaisesRegex(self, ImproperlyConfigured, "'ValidationTestModelAdmin.ordering\[0\]' refers to field 'non_existent_field' that is missing from model 'modeladmin.ValidationTestModel'.", - validate, - ValidationTestModelAdmin, + ValidationTestModelAdmin.validate, ValidationTestModel, ) @@ -1159,25 +1113,24 @@ class ValidationTests(unittest.TestCase): six.assertRaisesRegex(self, ImproperlyConfigured, "'ValidationTestModelAdmin.ordering' has the random ordering marker '\?', but contains other fields as well. Please either remove '\?' or the other fields.", - validate, - ValidationTestModelAdmin, + ValidationTestModelAdmin.validate, ValidationTestModel, ) class ValidationTestModelAdmin(ModelAdmin): ordering = ('?',) - validate(ValidationTestModelAdmin, ValidationTestModel) + ValidationTestModelAdmin.validate(ValidationTestModel) class ValidationTestModelAdmin(ModelAdmin): ordering = ('band__name',) - validate(ValidationTestModelAdmin, ValidationTestModel) + ValidationTestModelAdmin.validate(ValidationTestModel) class ValidationTestModelAdmin(ModelAdmin): ordering = ('name',) - validate(ValidationTestModelAdmin, ValidationTestModel) + ValidationTestModelAdmin.validate(ValidationTestModel) def test_list_select_related_validation(self): @@ -1186,16 +1139,15 @@ class ValidationTests(unittest.TestCase): six.assertRaisesRegex(self, ImproperlyConfigured, - "'ValidationTestModelAdmin.list_select_related' should be a boolean.", - validate, - ValidationTestModelAdmin, + "'ValidationTestModelAdmin.list_select_related' should be a bool.", + ValidationTestModelAdmin.validate, ValidationTestModel, ) class ValidationTestModelAdmin(ModelAdmin): list_select_related = False - validate(ValidationTestModelAdmin, ValidationTestModel) + ValidationTestModelAdmin.validate(ValidationTestModel) def test_save_as_validation(self): @@ -1204,16 +1156,15 @@ class ValidationTests(unittest.TestCase): six.assertRaisesRegex(self, ImproperlyConfigured, - "'ValidationTestModelAdmin.save_as' should be a boolean.", - validate, - ValidationTestModelAdmin, + "'ValidationTestModelAdmin.save_as' should be a bool.", + ValidationTestModelAdmin.validate, ValidationTestModel, ) class ValidationTestModelAdmin(ModelAdmin): save_as = True - validate(ValidationTestModelAdmin, ValidationTestModel) + ValidationTestModelAdmin.validate(ValidationTestModel) def test_save_on_top_validation(self): @@ -1222,16 +1173,15 @@ class ValidationTests(unittest.TestCase): six.assertRaisesRegex(self, ImproperlyConfigured, - "'ValidationTestModelAdmin.save_on_top' should be a boolean.", - validate, - ValidationTestModelAdmin, + "'ValidationTestModelAdmin.save_on_top' should be a bool.", + ValidationTestModelAdmin.validate, ValidationTestModel, ) class ValidationTestModelAdmin(ModelAdmin): save_on_top = True - validate(ValidationTestModelAdmin, ValidationTestModel) + ValidationTestModelAdmin.validate(ValidationTestModel) def test_inlines_validation(self): @@ -1241,8 +1191,7 @@ class ValidationTests(unittest.TestCase): six.assertRaisesRegex(self, ImproperlyConfigured, "'ValidationTestModelAdmin.inlines' must be a list or tuple.", - validate, - ValidationTestModelAdmin, + ValidationTestModelAdmin.validate, ValidationTestModel, ) @@ -1255,8 +1204,7 @@ class ValidationTests(unittest.TestCase): six.assertRaisesRegex(self, ImproperlyConfigured, "'ValidationTestModelAdmin.inlines\[0\]' does not inherit from BaseModelAdmin.", - validate, - ValidationTestModelAdmin, + ValidationTestModelAdmin.validate, ValidationTestModel, ) @@ -1269,8 +1217,7 @@ class ValidationTests(unittest.TestCase): six.assertRaisesRegex(self, ImproperlyConfigured, "'model' is a required attribute of 'ValidationTestModelAdmin.inlines\[0\]'.", - validate, - ValidationTestModelAdmin, + ValidationTestModelAdmin.validate, ValidationTestModel, ) @@ -1286,8 +1233,7 @@ class ValidationTests(unittest.TestCase): six.assertRaisesRegex(self, ImproperlyConfigured, "'ValidationTestModelAdmin.inlines\[0\].model' does not inherit from models.Model.", - validate, - ValidationTestModelAdmin, + ValidationTestModelAdmin.validate, ValidationTestModel, ) @@ -1297,7 +1243,7 @@ class ValidationTests(unittest.TestCase): class ValidationTestModelAdmin(ModelAdmin): inlines = [ValidationTestInline] - validate(ValidationTestModelAdmin, ValidationTestModel) + ValidationTestModelAdmin.validate(ValidationTestModel) def test_fields_validation(self): @@ -1311,8 +1257,7 @@ class ValidationTests(unittest.TestCase): six.assertRaisesRegex(self, ImproperlyConfigured, "'ValidationTestInline.fields' must be a list or tuple.", - validate, - ValidationTestModelAdmin, + ValidationTestModelAdmin.validate, ValidationTestModel, ) @@ -1328,8 +1273,7 @@ class ValidationTests(unittest.TestCase): six.assertRaisesRegex(self, ImproperlyConfigured, "'ValidationTestInline.fk_name' refers to field 'non_existent_field' that is missing from model 'modeladmin.ValidationTestInlineModel'.", - validate, - ValidationTestModelAdmin, + ValidationTestModelAdmin.validate, ValidationTestModel, ) @@ -1340,7 +1284,7 @@ class ValidationTests(unittest.TestCase): class ValidationTestModelAdmin(ModelAdmin): inlines = [ValidationTestInline] - validate(ValidationTestModelAdmin, ValidationTestModel) + ValidationTestModelAdmin.validate(ValidationTestModel) def test_extra_validation(self): @@ -1353,9 +1297,8 @@ class ValidationTests(unittest.TestCase): six.assertRaisesRegex(self, ImproperlyConfigured, - "'ValidationTestInline.extra' should be a integer.", - validate, - ValidationTestModelAdmin, + "'ValidationTestInline.extra' should be a int.", + ValidationTestModelAdmin.validate, ValidationTestModel, ) @@ -1366,7 +1309,7 @@ class ValidationTests(unittest.TestCase): class ValidationTestModelAdmin(ModelAdmin): inlines = [ValidationTestInline] - validate(ValidationTestModelAdmin, ValidationTestModel) + ValidationTestModelAdmin.validate(ValidationTestModel) def test_max_num_validation(self): @@ -1379,9 +1322,8 @@ class ValidationTests(unittest.TestCase): six.assertRaisesRegex(self, ImproperlyConfigured, - "'ValidationTestInline.max_num' should be an integer or None \(default\).", - validate, - ValidationTestModelAdmin, + "'ValidationTestInline.max_num' should be a int.", + ValidationTestModelAdmin.validate, ValidationTestModel, ) @@ -1392,7 +1334,7 @@ class ValidationTests(unittest.TestCase): class ValidationTestModelAdmin(ModelAdmin): inlines = [ValidationTestInline] - validate(ValidationTestModelAdmin, ValidationTestModel) + ValidationTestModelAdmin.validate(ValidationTestModel) def test_formset_validation(self): @@ -1409,8 +1351,7 @@ class ValidationTests(unittest.TestCase): six.assertRaisesRegex(self, ImproperlyConfigured, "'ValidationTestInline.formset' does not inherit from BaseModelFormSet.", - validate, - ValidationTestModelAdmin, + ValidationTestModelAdmin.validate, ValidationTestModel, ) @@ -1424,4 +1365,4 @@ class ValidationTests(unittest.TestCase): class ValidationTestModelAdmin(ModelAdmin): inlines = [ValidationTestInline] - validate(ValidationTestModelAdmin, ValidationTestModel) + ValidationTestModelAdmin.validate(ValidationTestModel) -- cgit v1.3 From 23e1b59cf2ad6a75637dd0273973e657e48e317e Mon Sep 17 00:00:00 2001 From: Loic Bistuer Date: Wed, 17 Apr 2013 01:49:22 +0700 Subject: Fixed #18681 -- BaseModelAdmin.get_form and InlineModelAdmin.get_formset no longer bypass get_fieldsets Thanks msopacua for the report. --- django/contrib/admin/options.py | 16 ++++++++-------- tests/modeladmin/tests.py | 24 ++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 8 deletions(-) (limited to 'tests/modeladmin') diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py index a0a82f32ff..97f82cbb59 100644 --- a/django/contrib/admin/options.py +++ b/django/contrib/admin/options.py @@ -498,7 +498,7 @@ class ModelAdmin(BaseModelAdmin): "Hook for specifying fieldsets for the add form." if self.declared_fieldsets: return self.declared_fieldsets - form = self.get_form(request, obj) + form = self.get_form(request, obj, fields=None) fields = list(form.base_fields) + list(self.get_readonly_fields(request, obj)) return [(None, {'fields': fields})] @@ -507,10 +507,10 @@ class ModelAdmin(BaseModelAdmin): Returns a Form class for use in the admin add view. This is used by add_view and change_view. """ - if self.declared_fieldsets: - fields = flatten_fieldsets(self.declared_fieldsets) + if 'fields' in kwargs: + fields = kwargs.pop('fields') else: - fields = None + fields = flatten_fieldsets(self.get_fieldsets(request, obj)) if self.exclude is None: exclude = [] else: @@ -1518,10 +1518,10 @@ class InlineModelAdmin(BaseModelAdmin): def get_formset(self, request, obj=None, **kwargs): """Returns a BaseInlineFormSet class for use in admin add/change views.""" - if self.declared_fieldsets: - fields = flatten_fieldsets(self.declared_fieldsets) + if 'fields' in kwargs: + fields = kwargs.pop('fields') else: - fields = None + fields = flatten_fieldsets(self.get_fieldsets(request, obj)) if self.exclude is None: exclude = [] else: @@ -1593,7 +1593,7 @@ class InlineModelAdmin(BaseModelAdmin): def get_fieldsets(self, request, obj=None): if self.declared_fieldsets: return self.declared_fieldsets - form = self.get_formset(request, obj).form + form = self.get_formset(request, obj, fields=None).form fields = list(form.base_fields) + list(self.get_readonly_fields(request, obj)) return [(None, {'fields': fields})] diff --git a/tests/modeladmin/tests.py b/tests/modeladmin/tests.py index f89f1c20ec..73e88024cd 100644 --- a/tests/modeladmin/tests.py +++ b/tests/modeladmin/tests.py @@ -64,6 +64,30 @@ class ModelAdminTests(TestCase): self.assertEqual(ma.get_fieldsets(request, self.band), [(None, {'fields': ['name', 'bio', 'sign_date']})]) + def test_get_fieldsets(self): + # Test that get_fieldsets is called when figuring out form fields. + # Refs #18681. + + class BandAdmin(ModelAdmin): + def get_fieldsets(self, request, obj=None): + return [(None, {'fields': ['name', 'bio']})] + + ma = BandAdmin(Band, self.site) + form = ma.get_form(None) + self.assertEqual(form._meta.fields, ['name', 'bio']) + + class InlineBandAdmin(TabularInline): + model = Concert + fk_name = 'main_band' + can_delete = False + + def get_fieldsets(self, request, obj=None): + return [(None, {'fields': ['day', 'transport']})] + + ma = InlineBandAdmin(Band, self.site) + form = ma.get_formset(None).form + self.assertEqual(form._meta.fields, ['day', 'transport']) + def test_field_arguments(self): # If we specify the fields argument, fieldsets_add and fielsets_change should # just stick the fields into a formsets structure and return it. -- cgit v1.3 From 0fd9f7c95f748764867dc148a2bacef807466d85 Mon Sep 17 00:00:00 2001 From: Tomek Paczkowski Date: Tue, 4 Jun 2013 23:35:11 +0200 Subject: Fixed #19080 -- Fine-grained control over select_related in admin --- django/contrib/admin/validation.py | 10 +++++++-- django/contrib/admin/views/main.py | 44 +++++++++++++++++++++++--------------- docs/ref/contrib/admin/index.txt | 22 ++++++++++++++----- tests/admin_changelist/admin.py | 5 +++++ tests/admin_changelist/tests.py | 31 +++++++++++++++++++++++---- tests/modeladmin/tests.py | 6 ++++-- 6 files changed, 88 insertions(+), 30 deletions(-) (limited to 'tests/modeladmin') diff --git a/django/contrib/admin/validation.py b/django/contrib/admin/validation.py index 59c5ad35ef..222d433e53 100644 --- a/django/contrib/admin/validation.py +++ b/django/contrib/admin/validation.py @@ -310,8 +310,14 @@ class ModelAdminValidator(BaseValidator): % (cls.__name__, idx, field)) def validate_list_select_related(self, cls, model): - " Validate that list_select_related is a boolean. " - check_type(cls, 'list_select_related', bool) + " Validate that list_select_related is a boolean, a list or a tuple. " + list_select_related = getattr(cls, 'list_select_related', None) + if list_select_related: + types = (bool, tuple, list) + if not isinstance(list_select_related, types): + raise ImproperlyConfigured("'%s.list_select_related' should be " + "either a bool, a tuple or a list" % + cls.__name__) def validate_list_per_page(self, cls, model): " Validate that list_per_page is an integer. " diff --git a/django/contrib/admin/views/main.py b/django/contrib/admin/views/main.py index dbed21265c..8ea7e10fc0 100644 --- a/django/contrib/admin/views/main.py +++ b/django/contrib/admin/views/main.py @@ -356,36 +356,46 @@ class ChangeList(six.with_metaclass(RenameChangeListMethods)): # ValueError, ValidationError, or ?. raise IncorrectLookupParameters(e) - # Use select_related() if one of the list_display options is a field - # with a relationship and the provided queryset doesn't already have - # select_related defined. if not qs.query.select_related: - if self.list_select_related: - qs = qs.select_related() - else: - for field_name in self.list_display: - try: - field = self.lookup_opts.get_field(field_name) - except models.FieldDoesNotExist: - pass - else: - if isinstance(field.rel, models.ManyToOneRel): - qs = qs.select_related() - break + qs = self.apply_select_related(qs) # Set ordering. ordering = self.get_ordering(request, qs) qs = qs.order_by(*ordering) # Apply search results - qs, search_use_distinct = self.model_admin.get_search_results(request, qs, self.query) + qs, search_use_distinct = self.model_admin.get_search_results( + request, qs, self.query) - # Remove duplicates from results, if neccesary + # Remove duplicates from results, if necessary if filters_use_distinct | search_use_distinct: return qs.distinct() else: return qs + def apply_select_related(self, qs): + if self.list_select_related is True: + return qs.select_related() + + if self.list_select_related is False: + if self.has_related_field_in_list_display(): + return qs.select_related() + + if self.list_select_related: + return qs.select_related(*self.list_select_related) + return qs + + def has_related_field_in_list_display(self): + for field_name in self.list_display: + try: + field = self.lookup_opts.get_field(field_name) + except models.FieldDoesNotExist: + pass + else: + if isinstance(field.rel, models.ManyToOneRel): + return True + return False + def url_for_result(self, result): pk = getattr(result, self.pk_attname) return reverse('admin:%s_%s_change' % (self.opts.app_label, diff --git a/docs/ref/contrib/admin/index.txt b/docs/ref/contrib/admin/index.txt index 8f457e77ac..1a5e9f2073 100644 --- a/docs/ref/contrib/admin/index.txt +++ b/docs/ref/contrib/admin/index.txt @@ -812,12 +812,24 @@ subclass:: the list of objects on the admin change list page. This can save you a bunch of database queries. - The value should be either ``True`` or ``False``. Default is ``False``. + .. versionchanged:: dev - Note that Django will use - :meth:`~django.db.models.query.QuerySet.select_related`, - regardless of this setting if one of the ``list_display`` fields is a - ``ForeignKey``. + The value should be either a boolean, a list or a tuple. Default is + ``False``. + + When value is ``True``, ``select_related()`` will always be called. When + value is set to ``False``, Django will look at ``list_display`` and call + ``select_related()`` if any ``ForeignKey`` is present. + + If you need more fine-grained control, use a tuple (or list) as value for + ``list_select_related``. Empty tuple will prevent Django from calling + ``select_related`` at all. Any other tuple will be passed directly to + ``select_related`` as parameters. For example:: + + class ArticleAdmin(admin.ModelAdmin): + list_select_related = ('author', 'category') + + will call ``select_related('author', 'category')``. .. attribute:: ModelAdmin.ordering diff --git a/tests/admin_changelist/admin.py b/tests/admin_changelist/admin.py index 8387ba77a1..175b1972c9 100644 --- a/tests/admin_changelist/admin.py +++ b/tests/admin_changelist/admin.py @@ -67,6 +67,11 @@ class ChordsBandAdmin(admin.ModelAdmin): list_filter = ['members'] +class InvitationAdmin(admin.ModelAdmin): + list_display = ('band', 'player') + list_select_related = ('player',) + + class DynamicListDisplayChildAdmin(admin.ModelAdmin): list_display = ('parent', 'name', 'age') diff --git a/tests/admin_changelist/tests.py b/tests/admin_changelist/tests.py index 38b78faaf4..7f3f0d162e 100644 --- a/tests/admin_changelist/tests.py +++ b/tests/admin_changelist/tests.py @@ -18,7 +18,7 @@ from .admin import (ChildAdmin, QuartetAdmin, BandAdmin, ChordsBandAdmin, GroupAdmin, ParentAdmin, DynamicListDisplayChildAdmin, DynamicListDisplayLinksChildAdmin, CustomPaginationAdmin, FilteredChildAdmin, CustomPaginator, site as custom_site, - SwallowAdmin, DynamicListFilterChildAdmin) + SwallowAdmin, DynamicListFilterChildAdmin, InvitationAdmin) from .models import (Event, Child, Parent, Genre, Band, Musician, Group, Quartet, Membership, ChordsMusician, ChordsBand, Invitation, Swallow, UnorderedObject, OrderedObject, CustomIdUser) @@ -46,9 +46,32 @@ class ChangeListTests(TestCase): m = ChildAdmin(Child, admin.site) request = self.factory.get('/child/') cl = ChangeList(request, Child, m.list_display, m.list_display_links, - m.list_filter, m.date_hierarchy, m.search_fields, - m.list_select_related, m.list_per_page, m.list_max_show_all, m.list_editable, m) - self.assertEqual(cl.queryset.query.select_related, {'parent': {'name': {}}}) + m.list_filter, m.date_hierarchy, m.search_fields, + m.list_select_related, m.list_per_page, + m.list_max_show_all, m.list_editable, m) + self.assertEqual(cl.queryset.query.select_related, { + 'parent': {'name': {}} + }) + + def test_select_related_as_tuple(self): + ia = InvitationAdmin(Invitation, admin.site) + request = self.factory.get('/invitation/') + cl = ChangeList(request, Child, ia.list_display, ia.list_display_links, + ia.list_filter, ia.date_hierarchy, ia.search_fields, + ia.list_select_related, ia.list_per_page, + ia.list_max_show_all, ia.list_editable, ia) + self.assertEqual(cl.queryset.query.select_related, {'player': {}}) + + def test_select_related_as_empty_tuple(self): + ia = InvitationAdmin(Invitation, admin.site) + ia.list_select_related = () + request = self.factory.get('/invitation/') + cl = ChangeList(request, Child, ia.list_display, ia.list_display_links, + ia.list_filter, ia.date_hierarchy, ia.search_fields, + ia.list_select_related, ia.list_per_page, + ia.list_max_show_all, ia.list_editable, ia) + self.assertEqual(cl.queryset.query.select_related, False) + def test_result_list_empty_changelist_value(self): """ diff --git a/tests/modeladmin/tests.py b/tests/modeladmin/tests.py index 73e88024cd..805b57c070 100644 --- a/tests/modeladmin/tests.py +++ b/tests/modeladmin/tests.py @@ -1161,9 +1161,11 @@ class ValidationTests(unittest.TestCase): class ValidationTestModelAdmin(ModelAdmin): list_select_related = 1 - six.assertRaisesRegex(self, + six.assertRaisesRegex( + self, ImproperlyConfigured, - "'ValidationTestModelAdmin.list_select_related' should be a bool.", + "'ValidationTestModelAdmin.list_select_related' should be either a " + "bool, a tuple or a list", ValidationTestModelAdmin.validate, ValidationTestModel, ) -- cgit v1.3