diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py index 5e7b23f9a0..d64a2e9a28 100644 --- a/django/contrib/admin/options.py +++ b/django/contrib/admin/options.py @@ -327,6 +327,10 @@ class BaseModelAdmin(metaclass=forms.MediaDefiningClass): return self.fieldsets return [(None, {'fields': self.get_fields(request, obj)})] + def get_inlines(self, request, obj): + """Hook for specifying custom inlines.""" + return self.inlines + def get_ordering(self, request): """ Hook for specifying field ordering. @@ -582,7 +586,7 @@ class ModelAdmin(BaseModelAdmin): def get_inline_instances(self, request, obj=None): inline_instances = [] - for inline_class in self.inlines: + for inline_class in self.get_inlines(request, obj): inline = inline_class(self.model, self.admin_site) if request: if not (inline.has_view_or_change_permission(request, obj) or diff --git a/docs/ref/contrib/admin/index.txt b/docs/ref/contrib/admin/index.txt index 93679c46fd..e6bb91cf5c 100644 --- a/docs/ref/contrib/admin/index.txt +++ b/docs/ref/contrib/admin/index.txt @@ -1627,6 +1627,16 @@ templates used by the :class:`ModelAdmin` views: instances of the classes defined in :attr:`inlines` or you might encounter a "Bad Request" error when adding related objects. +.. method:: ModelAdmin.get_inlines(request, obj) + + .. versionadded:: 3.0 + + The ``get_inlines`` method is given the ``HttpRequest`` and the + ``obj`` being edited (or ``None`` on an add form) and is expected to return + an iterable of inlines. You can override this method to dynamically add + inlines based on the request or model instance instead of specifying them + in :attr:`ModelAdmin.inlines`. + .. method:: ModelAdmin.get_urls() The ``get_urls`` method on a ``ModelAdmin`` returns the URLs to be used for diff --git a/docs/releases/3.0.txt b/docs/releases/3.0.txt index d7ad7a8cf7..44656b11cd 100644 --- a/docs/releases/3.0.txt +++ b/docs/releases/3.0.txt @@ -47,6 +47,10 @@ Minor features * Added support for the ``admin_order_field`` attribute on properties in :attr:`.ModelAdmin.list_display`. +* The new :meth:`ModelAdmin.get_inlines() + ` method allows specifying the + inlines based on the request or model instance. + :mod:`django.contrib.admindocs` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/tests/generic_inline_admin/tests.py b/tests/generic_inline_admin/tests.py index 36f1822805..b9f678a13a 100644 --- a/tests/generic_inline_admin/tests.py +++ b/tests/generic_inline_admin/tests.py @@ -429,3 +429,29 @@ class GenericInlineModelAdminTest(SimpleTestCase): inlines = ma.get_inline_instances(request) for (formset, inline), other_inline in zip(ma.get_formsets_with_inlines(request), inlines): self.assertIsInstance(formset, other_inline.get_formset(request).__class__) + + def test_get_inline_instances_override_get_inlines(self): + class MediaInline(GenericTabularInline): + model = Media + + class AlternateInline(GenericTabularInline): + model = Media + + class EpisodeAdmin(admin.ModelAdmin): + inlines = (AlternateInline, MediaInline) + + def get_inlines(self, request, obj): + if hasattr(request, 'name'): + if request.name == 'alternate': + return self.inlines[:1] + elif request.name == 'media': + return self.inlines[1:2] + return [] + + ma = EpisodeAdmin(Episode, self.site) + self.assertEqual(ma.get_inlines(request, None), []) + self.assertEqual(ma.get_inline_instances(request), []) + for name, inline_class in (('alternate', AlternateInline), ('media', MediaInline)): + request.name = name + self.assertEqual(ma.get_inlines(request, None), (inline_class,)), + self.assertEqual(type(ma.get_inline_instances(request)[0]), inline_class)