from django import forms from django.contrib.admin import BooleanFieldListFilter, SimpleListFilter from django.contrib.admin.options import VERTICAL, ModelAdmin, TabularInline from django.contrib.admin.sites import AdminSite from django.core.checks import Error from django.db.models import F from django.db.models.functions import Upper from django.forms.models import BaseModelFormSet from django.test import SimpleTestCase from .models import ( Band, Song, User, ValidationTestInlineModel, ValidationTestModel, ) class CheckTestCase(SimpleTestCase): def assertIsInvalid(self, model_admin, model, msg, id=None, hint=None, invalid_obj=None, admin_site=None): if admin_site is None: admin_site = AdminSite() invalid_obj = invalid_obj or model_admin admin_obj = model_admin(model, admin_site) self.assertEqual(admin_obj.check(), [Error(msg, hint=hint, obj=invalid_obj, id=id)]) def assertIsInvalidRegexp(self, model_admin, model, msg, id=None, hint=None, invalid_obj=None): """ Same as assertIsInvalid but treats the given msg as a regexp. """ invalid_obj = invalid_obj or model_admin admin_obj = model_admin(model, AdminSite()) errors = admin_obj.check() self.assertEqual(len(errors), 1) error = errors[0] self.assertEqual(error.hint, hint) self.assertEqual(error.obj, invalid_obj) self.assertEqual(error.id, id) self.assertRegex(error.msg, msg) def assertIsValid(self, model_admin, model, admin_site=None): if admin_site is None: admin_site = AdminSite() admin_obj = model_admin(model, admin_site) self.assertEqual(admin_obj.check(), []) class RawIdCheckTests(CheckTestCase): def test_not_iterable(self): class TestModelAdmin(ModelAdmin): raw_id_fields = 10 self.assertIsInvalid( TestModelAdmin, ValidationTestModel, "The value of 'raw_id_fields' must be a list or tuple.", 'admin.E001' ) def test_missing_field(self): class TestModelAdmin(ModelAdmin): raw_id_fields = ('non_existent_field',) self.assertIsInvalid( TestModelAdmin, ValidationTestModel, "The value of 'raw_id_fields[0]' refers to 'non_existent_field', " "which is not an attribute of 'modeladmin.ValidationTestModel'.", 'admin.E002' ) def test_invalid_field_type(self): class TestModelAdmin(ModelAdmin): raw_id_fields = ('name',) self.assertIsInvalid( TestModelAdmin, ValidationTestModel, "The value of 'raw_id_fields[0]' must be a foreign key or a " "many-to-many field.", 'admin.E003' ) def test_valid_case(self): class TestModelAdmin(ModelAdmin): raw_id_fields = ('users',) self.assertIsValid(TestModelAdmin, ValidationTestModel) class FieldsetsCheckTests(CheckTestCase): def test_valid_case(self): class TestModelAdmin(ModelAdmin): fieldsets = (('General', {'fields': ('name',)}),) self.assertIsValid(TestModelAdmin, ValidationTestModel) def test_not_iterable(self): class TestModelAdmin(ModelAdmin): fieldsets = 10 self.assertIsInvalid( TestModelAdmin, ValidationTestModel, "The value of 'fieldsets' must be a list or tuple.", 'admin.E007' ) def test_non_iterable_item(self): class TestModelAdmin(ModelAdmin): fieldsets = ({},) self.assertIsInvalid( TestModelAdmin, ValidationTestModel, "The value of 'fieldsets[0]' must be a list or tuple.", 'admin.E008' ) def test_item_not_a_pair(self): class TestModelAdmin(ModelAdmin): fieldsets = ((),) self.assertIsInvalid( TestModelAdmin, ValidationTestModel, "The value of 'fieldsets[0]' must be of length 2.", 'admin.E009' ) def test_second_element_of_item_not_a_dict(self): class TestModelAdmin(ModelAdmin): fieldsets = (('General', ()),) self.assertIsInvalid( TestModelAdmin, ValidationTestModel, "The value of 'fieldsets[0][1]' must be a dictionary.", 'admin.E010' ) def test_missing_fields_key(self): class TestModelAdmin(ModelAdmin): fieldsets = (('General', {}),) self.assertIsInvalid( TestModelAdmin, ValidationTestModel, "The value of 'fieldsets[0][1]' must contain the key 'fields'.", 'admin.E011' ) class TestModelAdmin(ModelAdmin): fieldsets = (('General', {'fields': ('name',)}),) self.assertIsValid(TestModelAdmin, ValidationTestModel) def test_specified_both_fields_and_fieldsets(self): class TestModelAdmin(ModelAdmin): fieldsets = (('General', {'fields': ('name',)}),) fields = ['name'] self.assertIsInvalid( TestModelAdmin, ValidationTestModel, "Both 'fieldsets' and 'fields' are specified.", 'admin.E005' ) def test_duplicate_fields(self): class TestModelAdmin(ModelAdmin): fieldsets = [(None, {'fields': ['name', 'name']})] self.assertIsInvalid( TestModelAdmin, ValidationTestModel, "There are duplicate field(s) in 'fieldsets[0][1]'.", 'admin.E012' ) def test_duplicate_fields_in_fieldsets(self): class TestModelAdmin(ModelAdmin): fieldsets = [ (None, {'fields': ['name']}), (None, {'fields': ['name']}), ] self.assertIsInvalid( TestModelAdmin, ValidationTestModel, "There are duplicate field(s) in 'fieldsets[1][1]'.", 'admin.E012' ) def test_fieldsets_with_custom_form_validation(self): class BandAdmin(ModelAdmin): fieldsets = (('Band', {'fields': ('name',)}),) self.assertIsValid(BandAdmin, Band) class FieldsCheckTests(CheckTestCase): def test_duplicate_fields_in_fields(self): class TestModelAdmin(ModelAdmin): fields = ['name', 'name'] self.assertIsInvalid( TestModelAdmin, ValidationTestModel, "The value of 'fields' contains duplicate field(s).", 'admin.E006' ) def test_inline(self): class ValidationTestInline(TabularInline): model = ValidationTestInlineModel fields = 10 class TestModelAdmin(ModelAdmin): inlines = [ValidationTestInline] self.assertIsInvalid( TestModelAdmin, ValidationTestModel, "The value of 'fields' must be a list or tuple.", 'admin.E004', invalid_obj=ValidationTestInline ) class FormCheckTests(CheckTestCase): def test_invalid_type(self): class FakeForm: pass class TestModelAdmin(ModelAdmin): form = FakeForm self.assertIsInvalid( TestModelAdmin, ValidationTestModel, "The value of 'form' must inherit from 'BaseModelForm'.", 'admin.E016' ) def test_fieldsets_with_custom_form_validation(self): class BandAdmin(ModelAdmin): fieldsets = (('Band', {'fields': ('name',)}),) self.assertIsValid(BandAdmin, Band) def test_valid_case(self): class AdminBandForm(forms.ModelForm): delete = forms.BooleanField() class BandAdmin(ModelAdmin): form = AdminBandForm fieldsets = ( ('Band', { 'fields': ('name', 'bio', 'sign_date', 'delete') }), ) self.assertIsValid(BandAdmin, Band) class FilterVerticalCheckTests(CheckTestCase): def test_not_iterable(self): class TestModelAdmin(ModelAdmin): filter_vertical = 10 self.assertIsInvalid( TestModelAdmin, ValidationTestModel, "The value of 'filter_vertical' must be a list or tuple.", 'admin.E017' ) def test_missing_field(self): class TestModelAdmin(ModelAdmin): filter_vertical = ('non_existent_field',) self.assertIsInvalid( TestModelAdmin, ValidationTestModel, "The value of 'filter_vertical[0]' refers to 'non_existent_field', " "which is not an attribute of 'modeladmin.ValidationTestModel'.", 'admin.E019' ) def test_invalid_field_type(self): class TestModelAdmin(ModelAdmin): filter_vertical = ('name',) self.assertIsInvalid( TestModelAdmin, ValidationTestModel, "The value of 'filter_vertical[0]' must be a many-to-many field.", 'admin.E020' ) def test_valid_case(self): class TestModelAdmin(ModelAdmin): filter_vertical = ('users',) self.assertIsValid(TestModelAdmin, ValidationTestModel) class FilterHorizontalCheckTests(CheckTestCase): def test_not_iterable(self): class TestModelAdmin(ModelAdmin): filter_horizontal = 10 self.assertIsInvalid( TestModelAdmin, ValidationTestModel, "The value of 'filter_horizontal' must be a list or tuple.", 'admin.E018' ) def test_missing_field(self): class TestModelAdmin(ModelAdmin): filter_horizontal = ('non_existent_field',) self.assertIsInvalid( TestModelAdmin, ValidationTestModel, "The value of 'filter_horizontal[0]' refers to 'non_existent_field', " "which is not an attribute of 'modeladmin.ValidationTestModel'.", 'admin.E019' ) def test_invalid_field_type(self): class TestModelAdmin(ModelAdmin): filter_horizontal = ('name',) self.assertIsInvalid( TestModelAdmin, ValidationTestModel, "The value of 'filter_horizontal[0]' must be a many-to-many field.", 'admin.E020' ) def test_valid_case(self): class TestModelAdmin(ModelAdmin): filter_horizontal = ('users',) self.assertIsValid(TestModelAdmin, ValidationTestModel) class RadioFieldsCheckTests(CheckTestCase): def test_not_dictionary(self): class TestModelAdmin(ModelAdmin): radio_fields = () self.assertIsInvalid( TestModelAdmin, ValidationTestModel, "The value of 'radio_fields' must be a dictionary.", 'admin.E021' ) def test_missing_field(self): class TestModelAdmin(ModelAdmin): radio_fields = {'non_existent_field': VERTICAL} self.assertIsInvalid( TestModelAdmin, ValidationTestModel, "The value of 'radio_fields' refers to 'non_existent_field', " "which is not an attribute of 'modeladmin.ValidationTestModel'.", 'admin.E022' ) def test_invalid_field_type(self): class TestModelAdmin(ModelAdmin): radio_fields = {'name': VERTICAL} self.assertIsInvalid( TestModelAdmin, ValidationTestModel, "The value of 'radio_fields' refers to 'name', which is not an instance " "of ForeignKey, and does not have a 'choices' definition.", 'admin.E023' ) def test_invalid_value(self): class TestModelAdmin(ModelAdmin): radio_fields = {'state': None} self.assertIsInvalid( TestModelAdmin, ValidationTestModel, "The value of 'radio_fields[\"state\"]' must be either admin.HORIZONTAL or admin.VERTICAL.", 'admin.E024' ) def test_valid_case(self): class TestModelAdmin(ModelAdmin): radio_fields = {'state': VERTICAL} self.assertIsValid(TestModelAdmin, ValidationTestModel) class PrepopulatedFieldsCheckTests(CheckTestCase): def test_not_list_or_tuple(self): class TestModelAdmin(ModelAdmin): prepopulated_fields = {'slug': 'test'} self.assertIsInvalid( TestModelAdmin, ValidationTestModel, 'The value of \'prepopulated_fields["slug"]\' must be a list ' 'or tuple.', 'admin.E029' ) def test_not_dictionary(self): class TestModelAdmin(ModelAdmin): prepopulated_fields = () self.assertIsInvalid( TestModelAdmin, ValidationTestModel, "The value of 'prepopulated_fields' must be a dictionary.", 'admin.E026' ) def test_missing_field(self): class TestModelAdmin(ModelAdmin): prepopulated_fields = {'non_existent_field': ('slug',)} self.assertIsInvalid( TestModelAdmin, ValidationTestModel, "The value of 'prepopulated_fields' refers to 'non_existent_field', " "which is not an attribute of 'modeladmin.ValidationTestModel'.", 'admin.E027' ) def test_missing_field_again(self): class TestModelAdmin(ModelAdmin): prepopulated_fields = {'slug': ('non_existent_field',)} self.assertIsInvalid( TestModelAdmin, ValidationTestModel, "The value of 'prepopulated_fields[\"slug\"][0]' refers to 'non_existent_field', " "which is not an attribute of 'modeladmin.ValidationTestModel'.", 'admin.E030' ) def test_invalid_field_type(self): class TestModelAdmin(ModelAdmin): prepopulated_fields = {'users': ('name',)} self.assertIsInvalid( TestModelAdmin, ValidationTestModel, "The value of 'prepopulated_fields' refers to 'users', which must not be " "a DateTimeField, a ForeignKey, a OneToOneField, or a ManyToManyField.", 'admin.E028' ) def test_valid_case(self): class TestModelAdmin(ModelAdmin): prepopulated_fields = {'slug': ('name',)} self.assertIsValid(TestModelAdmin, ValidationTestModel) def test_one_to_one_field(self): class TestModelAdmin(ModelAdmin): prepopulated_fields = {'best_friend': ('name',)} self.assertIsInvalid( TestModelAdmin, ValidationTestModel, "The value of 'prepopulated_fields' refers to 'best_friend', which must not be " "a DateTimeField, a ForeignKey, a OneToOneField, or a ManyToManyField.", 'admin.E028' ) class ListDisplayTests(CheckTestCase): def test_not_iterable(self): class TestModelAdmin(ModelAdmin): list_display = 10 self.assertIsInvalid( TestModelAdmin, ValidationTestModel, "The value of 'list_display' must be a list or tuple.", 'admin.E107' ) def test_missing_field(self): class TestModelAdmin(ModelAdmin): list_display = ('non_existent_field',) self.assertIsInvalid( TestModelAdmin, ValidationTestModel, "The value of 'list_display[0]' refers to 'non_existent_field', " "which is not a callable, an attribute of 'TestModelAdmin', " "or an attribute or method on 'modeladmin.ValidationTestModel'.", 'admin.E108' ) def test_invalid_field_type(self): class TestModelAdmin(ModelAdmin): list_display = ('users',) self.assertIsInvalid( TestModelAdmin, ValidationTestModel, "The value of 'list_display[0]' must not be a ManyToManyField.", 'admin.E109' ) def test_valid_case(self): def a_callable(obj): pass class TestModelAdmin(ModelAdmin): def a_method(self, obj): pass list_display = ('name', 'decade_published_in', 'a_method', a_callable) self.assertIsValid(TestModelAdmin, ValidationTestModel) class ListDisplayLinksCheckTests(CheckTestCase): def test_not_iterable(self): class TestModelAdmin(ModelAdmin): list_display_links = 10 self.assertIsInvalid( TestModelAdmin, ValidationTestModel, "The value of 'list_display_links' must be a list, a tuple, or None.", 'admin.E110' ) def test_missing_field(self): class TestModelAdmin(ModelAdmin): list_display_links = ('non_existent_field',) self.assertIsInvalid( TestModelAdmin, ValidationTestModel, ( "The value of 'list_display_links[0]' refers to " "'non_existent_field', which is not defined in 'list_display'." ), 'admin.E111' ) def test_missing_in_list_display(self): class TestModelAdmin(ModelAdmin): list_display_links = ('name',) self.assertIsInvalid( TestModelAdmin, ValidationTestModel, "The value of 'list_display_links[0]' refers to 'name', which is not defined in 'list_display'.", 'admin.E111' ) def test_valid_case(self): def a_callable(obj): pass class TestModelAdmin(ModelAdmin): def a_method(self, obj): pass list_display = ('name', 'decade_published_in', 'a_method', a_callable) list_display_links = ('name', 'decade_published_in', 'a_method', a_callable) self.assertIsValid(TestModelAdmin, ValidationTestModel) def test_None_is_valid_case(self): class TestModelAdmin(ModelAdmin): list_display_links = None self.assertIsValid(TestModelAdmin, ValidationTestModel) def test_list_display_links_check_skipped_if_get_list_display_overridden(self): """ list_display_links check is skipped if get_list_display() is overridden. """ class TestModelAdmin(ModelAdmin): list_display_links = ['name', 'subtitle'] def get_list_display(self, request): pass self.assertIsValid(TestModelAdmin, ValidationTestModel) def test_list_display_link_checked_for_list_tuple_if_get_list_display_overridden(self): """ list_display_links is checked for list/tuple/None even if get_list_display() is overridden. """ class TestModelAdmin(ModelAdmin): list_display_links = 'non-list/tuple' def get_list_display(self, request): pass self.assertIsInvalid( TestModelAdmin, ValidationTestModel, "The value of 'list_display_links' must be a list, a tuple, or None.", 'admin.E110' ) class ListFilterTests(CheckTestCase): def test_list_filter_validation(self): class TestModelAdmin(ModelAdmin): list_filter = 10 self.assertIsInvalid( TestModelAdmin, ValidationTestModel, "The value of 'list_filter' must be a list or tuple.", 'admin.E112' ) def test_missing_field(self): class TestModelAdmin(ModelAdmin): list_filter = ('non_existent_field',) self.assertIsInvalid( TestModelAdmin, ValidationTestModel, "The value of 'list_filter[0]' refers to 'non_existent_field', " "which does not refer to a Field.", 'admin.E116' ) def test_not_filter(self): class RandomClass: pass class TestModelAdmin(ModelAdmin): list_filter = (RandomClass,) self.assertIsInvalid( TestModelAdmin, ValidationTestModel, "The value of 'list_filter[0]' must inherit from 'ListFilter'.", 'admin.E113' ) def test_not_filter_again(self): class RandomClass: pass class TestModelAdmin(ModelAdmin): list_filter = (('is_active', RandomClass),) self.assertIsInvalid( TestModelAdmin, ValidationTestModel, "The value of 'list_filter[0][1]' must inherit from 'FieldListFilter'.", 'admin.E115' ) def test_not_filter_again_again(self): class AwesomeFilter(SimpleListFilter): def get_title(self): return 'awesomeness' def get_choices(self, request): return (('bit', 'A bit awesome'), ('very', 'Very awesome')) def get_queryset(self, cl, qs): return qs class TestModelAdmin(ModelAdmin): list_filter = (('is_active', AwesomeFilter),) self.assertIsInvalid( TestModelAdmin, ValidationTestModel, "The value of 'list_filter[0][1]' must inherit from 'FieldListFilter'.", 'admin.E115' ) def test_not_associated_with_field_name(self): class TestModelAdmin(ModelAdmin): list_filter = (BooleanFieldListFilter,) self.assertIsInvalid( TestModelAdmin, ValidationTestModel, "The value of 'list_filter[0]' must not inherit from 'FieldListFilter'.", 'admin.E114' ) def test_valid_case(self): class AwesomeFilter(SimpleListFilter): def get_title(self): return 'awesomeness' def get_choices(self, request): return (('bit', 'A bit awesome'), ('very', 'Very awesome')) def get_queryset(self, cl, qs): return qs class TestModelAdmin(ModelAdmin): list_filter = ('is_active', AwesomeFilter, ('is_active', BooleanFieldListFilter), 'no') self.assertIsValid(TestModelAdmin, ValidationTestModel) class ListPerPageCheckTests(CheckTestCase): def test_not_integer(self): class TestModelAdmin(ModelAdmin): list_per_page = 'hello' self.assertIsInvalid( TestModelAdmin, ValidationTestModel, "The value of 'list_per_page' must be an integer.", 'admin.E118' ) def test_valid_case(self): class TestModelAdmin(ModelAdmin): list_per_page = 100 self.assertIsValid(TestModelAdmin, ValidationTestModel) class ListMaxShowAllCheckTests(CheckTestCase): def test_not_integer(self): class TestModelAdmin(ModelAdmin): list_max_show_all = 'hello' self.assertIsInvalid( TestModelAdmin, ValidationTestModel, "The value of 'list_max_show_all' must be an integer.", 'admin.E119' ) def test_valid_case(self): class TestModelAdmin(ModelAdmin): list_max_show_all = 200 self.assertIsValid(TestModelAdmin, ValidationTestModel) class SearchFieldsCheckTests(CheckTestCase): def test_not_iterable(self): class TestModelAdmin(ModelAdmin): search_fields = 10 self.assertIsInvalid( TestModelAdmin, ValidationTestModel, "The value of 'search_fields' must be a list or tuple.", 'admin.E126' ) class DateHierarchyCheckTests(CheckTestCase): def test_missing_field(self): class TestModelAdmin(ModelAdmin): date_hierarchy = 'non_existent_field' self.assertIsInvalid( TestModelAdmin, ValidationTestModel, "The value of 'date_hierarchy' refers to 'non_existent_field', " "which does not refer to a Field.", 'admin.E127' ) def test_invalid_field_type(self): class TestModelAdmin(ModelAdmin): date_hierarchy = 'name' self.assertIsInvalid( TestModelAdmin, ValidationTestModel, "The value of 'date_hierarchy' must be a DateField or DateTimeField.", 'admin.E128' ) def test_valid_case(self): class TestModelAdmin(ModelAdmin): date_hierarchy = 'pub_date' self.assertIsValid(TestModelAdmin, ValidationTestModel) def test_related_valid_case(self): class TestModelAdmin(ModelAdmin): date_hierarchy = 'band__sign_date' self.assertIsValid(TestModelAdmin, ValidationTestModel) def test_related_invalid_field_type(self): class TestModelAdmin(ModelAdmin): date_hierarchy = 'band__name' self.assertIsInvalid( TestModelAdmin, ValidationTestModel, "The value of 'date_hierarchy' must be a DateField or DateTimeField.", 'admin.E128' ) class OrderingCheckTests(CheckTestCase): def test_not_iterable(self): class TestModelAdmin(ModelAdmin): ordering = 10 self.assertIsInvalid( TestModelAdmin, ValidationTestModel, "The value of 'ordering' must be a list or tuple.", 'admin.E031' ) class TestModelAdmin(ModelAdmin): ordering = ('non_existent_field',) self.assertIsInvalid( TestModelAdmin, ValidationTestModel, "The value of 'ordering[0]' refers to 'non_existent_field', " "which is not an attribute of 'modeladmin.ValidationTestModel'.", 'admin.E033' ) def test_random_marker_not_alone(self): class TestModelAdmin(ModelAdmin): ordering = ('?', 'name') self.assertIsInvalid( TestModelAdmin, ValidationTestModel, "The value of 'ordering' has the random ordering marker '?', but contains " "other fields as well.", 'admin.E032', hint='Either remove the "?", or remove the other fields.' ) def test_valid_random_marker_case(self): class TestModelAdmin(ModelAdmin): ordering = ('?',) self.assertIsValid(TestModelAdmin, ValidationTestModel) def test_valid_complex_case(self): class TestModelAdmin(ModelAdmin): ordering = ('band__name',) self.assertIsValid(TestModelAdmin, ValidationTestModel) def test_valid_case(self): class TestModelAdmin(ModelAdmin): ordering = ('name', 'pk') self.assertIsValid(TestModelAdmin, ValidationTestModel) def test_invalid_expression(self): class TestModelAdmin(ModelAdmin): ordering = (F('nonexistent'), ) self.assertIsInvalid( TestModelAdmin, ValidationTestModel, "The value of 'ordering[0]' refers to 'nonexistent', which is not " "an attribute of 'modeladmin.ValidationTestModel'.", 'admin.E033' ) def test_valid_expression(self): class TestModelAdmin(ModelAdmin): ordering = (Upper('name'), Upper('band__name').desc()) self.assertIsValid(TestModelAdmin, ValidationTestModel) class ListSelectRelatedCheckTests(CheckTestCase): def test_invalid_type(self): class TestModelAdmin(ModelAdmin): list_select_related = 1 self.assertIsInvalid( TestModelAdmin, ValidationTestModel, "The value of 'list_select_related' must be a boolean, tuple or list.", 'admin.E117' ) def test_valid_case(self): class TestModelAdmin(ModelAdmin): list_select_related = False self.assertIsValid(TestModelAdmin, ValidationTestModel) class SaveAsCheckTests(CheckTestCase): def test_not_boolean(self): class TestModelAdmin(ModelAdmin): save_as = 1 self.assertIsInvalid( TestModelAdmin, ValidationTestModel, "The value of 'save_as' must be a boolean.", 'admin.E101' ) def test_valid_case(self): class TestModelAdmin(ModelAdmin): save_as = True self.assertIsValid(TestModelAdmin, ValidationTestModel) class SaveOnTopCheckTests(CheckTestCase): def test_not_boolean(self): class TestModelAdmin(ModelAdmin): save_on_top = 1 self.assertIsInvalid( TestModelAdmin, ValidationTestModel, "The value of 'save_on_top' must be a boolean.", 'admin.E102' ) def test_valid_case(self): class TestModelAdmin(ModelAdmin): save_on_top = True self.assertIsValid(TestModelAdmin, ValidationTestModel) class InlinesCheckTests(CheckTestCase): def test_not_iterable(self): class TestModelAdmin(ModelAdmin): inlines = 10 self.assertIsInvalid( TestModelAdmin, ValidationTestModel, "The value of 'inlines' must be a list or tuple.", 'admin.E103' ) def test_not_model_admin(self): class ValidationTestInline: pass class TestModelAdmin(ModelAdmin): inlines = [ValidationTestInline] self.assertIsInvalidRegexp( TestModelAdmin, ValidationTestModel, r"'.*\.ValidationTestInline' must inherit from 'InlineModelAdmin'\.", 'admin.E104' ) def test_missing_model_field(self): class ValidationTestInline(TabularInline): pass class TestModelAdmin(ModelAdmin): inlines = [ValidationTestInline] self.assertIsInvalidRegexp( TestModelAdmin, ValidationTestModel, r"'.*\.ValidationTestInline' must have a 'model' attribute\.", 'admin.E105' ) def test_invalid_model_type(self): class SomethingBad: pass class ValidationTestInline(TabularInline): model = SomethingBad class TestModelAdmin(ModelAdmin): inlines = [ValidationTestInline] self.assertIsInvalidRegexp( TestModelAdmin, ValidationTestModel, r"The value of '.*\.ValidationTestInline.model' must be a Model\.", 'admin.E106' ) def test_valid_case(self): class ValidationTestInline(TabularInline): model = ValidationTestInlineModel class TestModelAdmin(ModelAdmin): inlines = [ValidationTestInline] self.assertIsValid(TestModelAdmin, ValidationTestModel) class FkNameCheckTests(CheckTestCase): def test_missing_field(self): class ValidationTestInline(TabularInline): model = ValidationTestInlineModel fk_name = 'non_existent_field' class TestModelAdmin(ModelAdmin): inlines = [ValidationTestInline] self.assertIsInvalid( TestModelAdmin, ValidationTestModel, "'modeladmin.ValidationTestInlineModel' has no field named 'non_existent_field'.", 'admin.E202', invalid_obj=ValidationTestInline ) def test_valid_case(self): class ValidationTestInline(TabularInline): model = ValidationTestInlineModel fk_name = 'parent' class TestModelAdmin(ModelAdmin): inlines = [ValidationTestInline] self.assertIsValid(TestModelAdmin, ValidationTestModel) class ExtraCheckTests(CheckTestCase): def test_not_integer(self): class ValidationTestInline(TabularInline): model = ValidationTestInlineModel extra = 'hello' class TestModelAdmin(ModelAdmin): inlines = [ValidationTestInline] self.assertIsInvalid( TestModelAdmin, ValidationTestModel, "The value of 'extra' must be an integer.", 'admin.E203', invalid_obj=ValidationTestInline ) def test_valid_case(self): class ValidationTestInline(TabularInline): model = ValidationTestInlineModel extra = 2 class TestModelAdmin(ModelAdmin): inlines = [ValidationTestInline] self.assertIsValid(TestModelAdmin, ValidationTestModel) class MaxNumCheckTests(CheckTestCase): def test_not_integer(self): class ValidationTestInline(TabularInline): model = ValidationTestInlineModel max_num = 'hello' class TestModelAdmin(ModelAdmin): inlines = [ValidationTestInline] self.assertIsInvalid( TestModelAdmin, ValidationTestModel, "The value of 'max_num' must be an integer.", 'admin.E204', invalid_obj=ValidationTestInline ) def test_valid_case(self): class ValidationTestInline(TabularInline): model = ValidationTestInlineModel max_num = 2 class TestModelAdmin(ModelAdmin): inlines = [ValidationTestInline] self.assertIsValid(TestModelAdmin, ValidationTestModel) class MinNumCheckTests(CheckTestCase): def test_not_integer(self): class ValidationTestInline(TabularInline): model = ValidationTestInlineModel min_num = 'hello' class TestModelAdmin(ModelAdmin): inlines = [ValidationTestInline] self.assertIsInvalid( TestModelAdmin, ValidationTestModel, "The value of 'min_num' must be an integer.", 'admin.E205', invalid_obj=ValidationTestInline ) def test_valid_case(self): class ValidationTestInline(TabularInline): model = ValidationTestInlineModel min_num = 2 class TestModelAdmin(ModelAdmin): inlines = [ValidationTestInline] self.assertIsValid(TestModelAdmin, ValidationTestModel) class FormsetCheckTests(CheckTestCase): def test_invalid_type(self): class FakeFormSet: pass class ValidationTestInline(TabularInline): model = ValidationTestInlineModel formset = FakeFormSet class TestModelAdmin(ModelAdmin): inlines = [ValidationTestInline] self.assertIsInvalid( TestModelAdmin, ValidationTestModel, "The value of 'formset' must inherit from 'BaseModelFormSet'.", 'admin.E206', invalid_obj=ValidationTestInline ) def test_valid_case(self): class RealModelFormSet(BaseModelFormSet): pass class ValidationTestInline(TabularInline): model = ValidationTestInlineModel formset = RealModelFormSet class TestModelAdmin(ModelAdmin): inlines = [ValidationTestInline] self.assertIsValid(TestModelAdmin, ValidationTestModel) class ListDisplayEditableTests(CheckTestCase): def test_list_display_links_is_none(self): """ list_display and list_editable can contain the same values when list_display_links is None """ class ProductAdmin(ModelAdmin): list_display = ['name', 'slug', 'pub_date'] list_editable = list_display list_display_links = None self.assertIsValid(ProductAdmin, ValidationTestModel) def test_list_display_first_item_same_as_list_editable_first_item(self): """ The first item in list_display can be the same as the first in list_editable. """ class ProductAdmin(ModelAdmin): list_display = ['name', 'slug', 'pub_date'] list_editable = ['name', 'slug'] list_display_links = ['pub_date'] self.assertIsValid(ProductAdmin, ValidationTestModel) def test_list_display_first_item_in_list_editable(self): """ The first item in list_display can be in list_editable as long as list_display_links is defined. """ class ProductAdmin(ModelAdmin): list_display = ['name', 'slug', 'pub_date'] list_editable = ['slug', 'name'] list_display_links = ['pub_date'] self.assertIsValid(ProductAdmin, ValidationTestModel) def test_list_display_first_item_same_as_list_editable_no_list_display_links(self): """ The first item in list_display cannot be the same as the first item in list_editable if list_display_links is not defined. """ class ProductAdmin(ModelAdmin): list_display = ['name'] list_editable = ['name'] self.assertIsInvalid( ProductAdmin, ValidationTestModel, "The value of 'list_editable[0]' refers to the first field " "in 'list_display' ('name'), which cannot be used unless " "'list_display_links' is set.", id='admin.E124', ) def test_list_display_first_item_in_list_editable_no_list_display_links(self): """ The first item in list_display cannot be in list_editable if list_display_links isn't defined. """ class ProductAdmin(ModelAdmin): list_display = ['name', 'slug', 'pub_date'] list_editable = ['slug', 'name'] self.assertIsInvalid( ProductAdmin, ValidationTestModel, "The value of 'list_editable[1]' refers to the first field " "in 'list_display' ('name'), which cannot be used unless " "'list_display_links' is set.", id='admin.E124', ) def test_both_list_editable_and_list_display_links(self): class ProductAdmin(ModelAdmin): list_editable = ('name',) list_display = ('name',) list_display_links = ('name',) self.assertIsInvalid( ProductAdmin, ValidationTestModel, "The value of 'name' cannot be in both 'list_editable' and " "'list_display_links'.", id='admin.E123', ) class AutocompleteFieldsTests(CheckTestCase): def test_autocomplete_e036(self): class Admin(ModelAdmin): autocomplete_fields = 'name' self.assertIsInvalid( Admin, Band, msg="The value of 'autocomplete_fields' must be a list or tuple.", id='admin.E036', invalid_obj=Admin, ) def test_autocomplete_e037(self): class Admin(ModelAdmin): autocomplete_fields = ('nonexistent',) self.assertIsInvalid( Admin, ValidationTestModel, msg=( "The value of 'autocomplete_fields[0]' refers to 'nonexistent', " "which is not an attribute of 'modeladmin.ValidationTestModel'." ), id='admin.E037', invalid_obj=Admin, ) def test_autocomplete_e38(self): class Admin(ModelAdmin): autocomplete_fields = ('name',) self.assertIsInvalid( Admin, ValidationTestModel, msg=( "The value of 'autocomplete_fields[0]' must be a foreign " "key or a many-to-many field." ), id='admin.E038', invalid_obj=Admin, ) def test_autocomplete_e039(self): class Admin(ModelAdmin): autocomplete_fields = ('band',) self.assertIsInvalid( Admin, Song, msg=( 'An admin for model "Band" has to be registered ' 'to be referenced by Admin.autocomplete_fields.' ), id='admin.E039', invalid_obj=Admin, ) def test_autocomplete_e040(self): class NoSearchFieldsAdmin(ModelAdmin): pass class AutocompleteAdmin(ModelAdmin): autocomplete_fields = ('featuring',) site = AdminSite() site.register(Band, NoSearchFieldsAdmin) self.assertIsInvalid( AutocompleteAdmin, Song, msg=( 'NoSearchFieldsAdmin must define "search_fields", because ' 'it\'s referenced by AutocompleteAdmin.autocomplete_fields.' ), id='admin.E040', invalid_obj=AutocompleteAdmin, admin_site=site, ) def test_autocomplete_is_valid(self): class SearchFieldsAdmin(ModelAdmin): search_fields = 'name' class AutocompleteAdmin(ModelAdmin): autocomplete_fields = ('featuring',) site = AdminSite() site.register(Band, SearchFieldsAdmin) self.assertIsValid(AutocompleteAdmin, Song, admin_site=site) def test_autocomplete_is_onetoone(self): class UserAdmin(ModelAdmin): search_fields = ('name',) class Admin(ModelAdmin): autocomplete_fields = ('best_friend',) site = AdminSite() site.register(User, UserAdmin) self.assertIsValid(Admin, ValidationTestModel, admin_site=site) class ActionsCheckTests(CheckTestCase): def test_custom_permissions_require_matching_has_method(self): def custom_permission_action(modeladmin, request, queryset): pass custom_permission_action.allowed_permissions = ('custom',) class BandAdmin(ModelAdmin): actions = (custom_permission_action,) self.assertIsInvalid( BandAdmin, Band, 'BandAdmin must define a has_custom_permission() method for the ' 'custom_permission_action action.', id='admin.E129', ) def test_actions_not_unique(self): def action(modeladmin, request, queryset): pass class BandAdmin(ModelAdmin): actions = (action, action) self.assertIsInvalid( BandAdmin, Band, "__name__ attributes of actions defined in " ".BandAdmin'> must be unique.", id='admin.E130', ) def test_actions_unique(self): def action1(modeladmin, request, queryset): pass def action2(modeladmin, request, queryset): pass class BandAdmin(ModelAdmin): actions = (action1, action2) self.assertIsValid(BandAdmin, Band)