diff --git a/django/contrib/admin/validation.py b/django/contrib/admin/validation.py index 733f89d06f0..a02bb7a316f 100644 --- a/django/contrib/admin/validation.py +++ b/django/contrib/admin/validation.py @@ -6,7 +6,7 @@ from django.forms.models import (BaseModelForm, BaseModelFormSet, fields_for_mod from django.contrib.admin import ListFilter, FieldListFilter from django.contrib.admin.util import get_fields_from_path, NotRelationField from django.contrib.admin.options import (flatten_fieldsets, BaseModelAdmin, - HORIZONTAL, VERTICAL) + ModelAdmin, HORIZONTAL, VERTICAL) __all__ = ['validate'] @@ -388,12 +388,14 @@ def check_formfield(cls, model, opts, label, field): raise ImproperlyConfigured("'%s.%s' refers to field '%s' that " "is missing from the form." % (cls.__name__, label, field)) else: - fields = fields_for_model(model) - try: - fields[field] - except KeyError: - raise ImproperlyConfigured("'%s.%s' refers to field '%s' that " - "is missing from the form." % (cls.__name__, label, field)) + get_form_is_overridden = hasattr(cls, 'get_form') and cls.get_form != ModelAdmin.get_form + if not get_form_is_overridden: + fields = fields_for_model(model) + try: + fields[field] + except KeyError: + raise ImproperlyConfigured("'%s.%s' refers to field '%s' that " + "is missing from the form." % (cls.__name__, label, field)) def fetch_attr(cls, model, opts, label, field): try: diff --git a/docs/ref/contrib/admin/index.txt b/docs/ref/contrib/admin/index.txt index 1414f19e488..3c051301619 100644 --- a/docs/ref/contrib/admin/index.txt +++ b/docs/ref/contrib/admin/index.txt @@ -1096,6 +1096,13 @@ templates used by the :class:`ModelAdmin` views: changelist that will be linked to the change view, as described in the :attr:`ModelAdmin.list_display_links` section. +.. method:: ModelAdmin.get_fieldsets(self, request, obj=None) + + The ``get_fieldsets`` method is given the ``HttpRequest`` and the ``obj`` + being edited (or ``None`` on an add form) and is expected to return a list + of two-tuples, in which each two-tuple represents a ``
`` on the + admin form page, as described above in the :attr:`ModelAdmin.fieldsets` section. + .. method:: ModelAdmin.get_list_filter(self, request) .. versionadded:: 1.5 diff --git a/tests/regressiontests/admin_validation/tests.py b/tests/regressiontests/admin_validation/tests.py index be2500a803e..b9b42c6bb96 100644 --- a/tests/regressiontests/admin_validation/tests.py +++ b/tests/regressiontests/admin_validation/tests.py @@ -20,6 +20,18 @@ class InvalidFields(admin.ModelAdmin): form = SongForm fields = ['spam'] +class ValidFormFieldsets(admin.ModelAdmin): + def get_form(self, request, obj=None, **kwargs): + class ExtraFieldForm(SongForm): + name = forms.CharField(max_length=50) + return ExtraFieldForm + + fieldsets = ( + (None, { + 'fields': ('name',), + }), + ) + class ValidationTestCase(TestCase): def test_readonly_and_editable(self): @@ -42,6 +54,14 @@ class ValidationTestCase(TestCase): validate, InvalidFields, Song) + def test_custom_get_form_with_fieldsets(self): + """ + Ensure that the fieldsets validation is skipped when the ModelAdmin.get_form() method + is overridden. + Refs #19445. + """ + validate(ValidFormFieldsets, Song) + def test_exclude_values(self): """ Tests for basic validation of 'exclude' option values (#12689)