Fixed #24941 -- Added ModelAdmin.get_exclude().
Thanks Ola Sitarska for the initial patch.
This commit is contained in:
parent
c60feb6999
commit
bf91be83d5
|
@ -282,6 +282,12 @@ class BaseModelAdmin(six.with_metaclass(forms.MediaDefiningClass)):
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
return mark_safe(self.admin_site.empty_value_display)
|
return mark_safe(self.admin_site.empty_value_display)
|
||||||
|
|
||||||
|
def get_exclude(self, request, obj=None):
|
||||||
|
"""
|
||||||
|
Hook for specifying exclude.
|
||||||
|
"""
|
||||||
|
return self.exclude
|
||||||
|
|
||||||
def get_fields(self, request, obj=None):
|
def get_fields(self, request, obj=None):
|
||||||
"""
|
"""
|
||||||
Hook for specifying fields.
|
Hook for specifying fields.
|
||||||
|
@ -605,13 +611,11 @@ class ModelAdmin(BaseModelAdmin):
|
||||||
fields = kwargs.pop('fields')
|
fields = kwargs.pop('fields')
|
||||||
else:
|
else:
|
||||||
fields = flatten_fieldsets(self.get_fieldsets(request, obj))
|
fields = flatten_fieldsets(self.get_fieldsets(request, obj))
|
||||||
if self.exclude is None:
|
excluded = self.get_exclude(request, obj)
|
||||||
exclude = []
|
exclude = [] if excluded is None else list(excluded)
|
||||||
else:
|
|
||||||
exclude = list(self.exclude)
|
|
||||||
readonly_fields = self.get_readonly_fields(request, obj)
|
readonly_fields = self.get_readonly_fields(request, obj)
|
||||||
exclude.extend(readonly_fields)
|
exclude.extend(readonly_fields)
|
||||||
if self.exclude is None and hasattr(self.form, '_meta') and self.form._meta.exclude:
|
if excluded is None and hasattr(self.form, '_meta') and self.form._meta.exclude:
|
||||||
# Take the custom ModelForm's Meta.exclude into account only if the
|
# Take the custom ModelForm's Meta.exclude into account only if the
|
||||||
# ModelAdmin doesn't define its own.
|
# ModelAdmin doesn't define its own.
|
||||||
exclude.extend(self.form._meta.exclude)
|
exclude.extend(self.form._meta.exclude)
|
||||||
|
@ -1851,12 +1855,10 @@ class InlineModelAdmin(BaseModelAdmin):
|
||||||
fields = kwargs.pop('fields')
|
fields = kwargs.pop('fields')
|
||||||
else:
|
else:
|
||||||
fields = flatten_fieldsets(self.get_fieldsets(request, obj))
|
fields = flatten_fieldsets(self.get_fieldsets(request, obj))
|
||||||
if self.exclude is None:
|
excluded = self.get_exclude(request, obj)
|
||||||
exclude = []
|
exclude = [] if excluded is None else list(excluded)
|
||||||
else:
|
|
||||||
exclude = list(self.exclude)
|
|
||||||
exclude.extend(self.get_readonly_fields(request, obj))
|
exclude.extend(self.get_readonly_fields(request, obj))
|
||||||
if self.exclude is None and hasattr(self.form, '_meta') and self.form._meta.exclude:
|
if excluded is None and hasattr(self.form, '_meta') and self.form._meta.exclude:
|
||||||
# Take the custom ModelForm's Meta.exclude into account only if the
|
# Take the custom ModelForm's Meta.exclude into account only if the
|
||||||
# InlineModelAdmin doesn't define its own.
|
# InlineModelAdmin doesn't define its own.
|
||||||
exclude.extend(self.form._meta.exclude)
|
exclude.extend(self.form._meta.exclude)
|
||||||
|
|
|
@ -1459,6 +1459,14 @@ templates used by the :class:`ModelAdmin` views:
|
||||||
names on the changelist that will be linked to the change view, as described
|
names on the changelist that will be linked to the change view, as described
|
||||||
in the :attr:`ModelAdmin.list_display_links` section.
|
in the :attr:`ModelAdmin.list_display_links` section.
|
||||||
|
|
||||||
|
.. method:: ModelAdmin.get_exclude(request, obj=None)
|
||||||
|
|
||||||
|
.. versionadded:: 1.11
|
||||||
|
|
||||||
|
The ``get_exclude`` method is given the ``HttpRequest`` and the ``obj``
|
||||||
|
being edited (or ``None`` on an add form) and is expected to return a list
|
||||||
|
of fields, as described in :attr:`ModelAdmin.exclude`.
|
||||||
|
|
||||||
.. method:: ModelAdmin.get_fields(request, obj=None)
|
.. method:: ModelAdmin.get_fields(request, obj=None)
|
||||||
|
|
||||||
The ``get_fields`` method is given the ``HttpRequest`` and the ``obj``
|
The ``get_fields`` method is given the ``HttpRequest`` and the ``obj``
|
||||||
|
|
|
@ -68,6 +68,10 @@ Minor features
|
||||||
|
|
||||||
* :attr:`.ModelAdmin.date_hierarchy` can now reference fields across relations.
|
* :attr:`.ModelAdmin.date_hierarchy` can now reference fields across relations.
|
||||||
|
|
||||||
|
* The new :meth:`ModelAdmin.get_exclude()
|
||||||
|
<django.contrib.admin.ModelAdmin.get_exclude>` hook allows specifying the
|
||||||
|
exclude fields based on the request or model instance.
|
||||||
|
|
||||||
:mod:`django.contrib.admindocs`
|
:mod:`django.contrib.admindocs`
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
|
|
@ -52,6 +52,7 @@ class ModelAdminTests(TestCase):
|
||||||
self.assertEqual(list(ma.get_form(request).base_fields), ['name', 'bio', 'sign_date'])
|
self.assertEqual(list(ma.get_form(request).base_fields), ['name', 'bio', 'sign_date'])
|
||||||
self.assertEqual(list(ma.get_fields(request)), ['name', 'bio', 'sign_date'])
|
self.assertEqual(list(ma.get_fields(request)), ['name', 'bio', 'sign_date'])
|
||||||
self.assertEqual(list(ma.get_fields(request, self.band)), ['name', 'bio', 'sign_date'])
|
self.assertEqual(list(ma.get_fields(request, self.band)), ['name', 'bio', 'sign_date'])
|
||||||
|
self.assertIsNone(ma.get_exclude(request, self.band))
|
||||||
|
|
||||||
def test_default_fieldsets(self):
|
def test_default_fieldsets(self):
|
||||||
# fieldsets_add and fieldsets_change should return a special data structure that
|
# fieldsets_add and fieldsets_change should return a special data structure that
|
||||||
|
@ -279,6 +280,40 @@ class ModelAdminTests(TestCase):
|
||||||
['main_band', 'opening_band', 'day', 'id', 'DELETE']
|
['main_band', 'opening_band', 'day', 'id', 'DELETE']
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_overriding_get_exclude(self):
|
||||||
|
class BandAdmin(ModelAdmin):
|
||||||
|
def get_exclude(self, request, obj=None):
|
||||||
|
return ['name']
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
list(BandAdmin(Band, self.site).get_form(request).base_fields),
|
||||||
|
['bio', 'sign_date']
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_get_exclude_overrides_exclude(self):
|
||||||
|
class BandAdmin(ModelAdmin):
|
||||||
|
exclude = ['bio']
|
||||||
|
|
||||||
|
def get_exclude(self, request, obj=None):
|
||||||
|
return ['name']
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
list(BandAdmin(Band, self.site).get_form(request).base_fields),
|
||||||
|
['bio', 'sign_date']
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_get_exclude_takes_obj(self):
|
||||||
|
class BandAdmin(ModelAdmin):
|
||||||
|
def get_exclude(self, request, obj=None):
|
||||||
|
if obj:
|
||||||
|
return ['sign_date']
|
||||||
|
return ['name']
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
list(BandAdmin(Band, self.site).get_form(request, self.band).base_fields),
|
||||||
|
['name', 'bio']
|
||||||
|
)
|
||||||
|
|
||||||
def test_custom_form_validation(self):
|
def test_custom_form_validation(self):
|
||||||
# If we specify a form, it should use it allowing custom validation to work
|
# If we specify a form, it should use it allowing custom validation to work
|
||||||
# properly. This won't, however, break any of the admin widgets or media.
|
# properly. This won't, however, break any of the admin widgets or media.
|
||||||
|
@ -346,6 +381,52 @@ class ModelAdminTests(TestCase):
|
||||||
list(list(ma.get_formsets_with_inlines(request))[0][0]().forms[0].fields),
|
list(list(ma.get_formsets_with_inlines(request))[0][0]().forms[0].fields),
|
||||||
['main_band', 'day', 'transport', 'id', 'DELETE'])
|
['main_band', 'day', 'transport', 'id', 'DELETE'])
|
||||||
|
|
||||||
|
def test_formset_overriding_get_exclude_with_form_fields(self):
|
||||||
|
class AdminConcertForm(forms.ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = Concert
|
||||||
|
fields = ['main_band', 'opening_band', 'day', 'transport']
|
||||||
|
|
||||||
|
class ConcertInline(TabularInline):
|
||||||
|
form = AdminConcertForm
|
||||||
|
fk_name = 'main_band'
|
||||||
|
model = Concert
|
||||||
|
|
||||||
|
def get_exclude(self, request, obj=None):
|
||||||
|
return ['opening_band']
|
||||||
|
|
||||||
|
class BandAdmin(ModelAdmin):
|
||||||
|
inlines = [ConcertInline]
|
||||||
|
|
||||||
|
ma = BandAdmin(Band, self.site)
|
||||||
|
self.assertEqual(
|
||||||
|
list(list(ma.get_formsets_with_inlines(request))[0][0]().forms[0].fields),
|
||||||
|
['main_band', 'day', 'transport', 'id', 'DELETE']
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_formset_overriding_get_exclude_with_form_exclude(self):
|
||||||
|
class AdminConcertForm(forms.ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = Concert
|
||||||
|
exclude = ['day']
|
||||||
|
|
||||||
|
class ConcertInline(TabularInline):
|
||||||
|
form = AdminConcertForm
|
||||||
|
fk_name = 'main_band'
|
||||||
|
model = Concert
|
||||||
|
|
||||||
|
def get_exclude(self, request, obj=None):
|
||||||
|
return ['opening_band']
|
||||||
|
|
||||||
|
class BandAdmin(ModelAdmin):
|
||||||
|
inlines = [ConcertInline]
|
||||||
|
|
||||||
|
ma = BandAdmin(Band, self.site)
|
||||||
|
self.assertEqual(
|
||||||
|
list(list(ma.get_formsets_with_inlines(request))[0][0]().forms[0].fields),
|
||||||
|
['main_band', 'day', 'transport', 'id', 'DELETE']
|
||||||
|
)
|
||||||
|
|
||||||
def test_queryset_override(self):
|
def test_queryset_override(self):
|
||||||
# If we need to override the queryset of a ModelChoiceField in our custom form
|
# If we need to override the queryset of a ModelChoiceField in our custom form
|
||||||
# make sure that RelatedFieldWidgetWrapper doesn't mess that up.
|
# make sure that RelatedFieldWidgetWrapper doesn't mess that up.
|
||||||
|
|
Loading…
Reference in New Issue