from django.contrib.contenttypes.admin import GenericInlineModelAdmin from django.contrib.contenttypes.models import ContentType from django.utils.functional import cached_property from polymorphic.formsets import ( BaseGenericPolymorphicInlineFormSet, GenericPolymorphicFormSetChild, polymorphic_child_forms_factory, ) from .inlines import PolymorphicInlineModelAdmin class GenericPolymorphicInlineModelAdmin(PolymorphicInlineModelAdmin, GenericInlineModelAdmin): """ Base class for variation of inlines based on generic foreign keys. """ #: The formset class formset = BaseGenericPolymorphicInlineFormSet def get_formset(self, request, obj=None, **kwargs): """ Construct the generic inline formset class. """ # Construct the FormSet class. This is almost the same as parent version, # except that a different super is called so generic_inlineformset_factory() is used. # NOTE that generic_inlineformset_factory() also makes sure the GFK fields are excluded in the form. FormSet = GenericInlineModelAdmin.get_formset(self, request, obj=obj, **kwargs) FormSet.child_forms = polymorphic_child_forms_factory( formset_children=self.get_formset_children(request, obj=obj) ) return FormSet class Child(PolymorphicInlineModelAdmin.Child): """ Variation for generic inlines. """ # Make sure that the GFK fields are excluded from the child forms formset_child = GenericPolymorphicFormSetChild ct_field = "content_type" ct_fk_field = "object_id" @cached_property def content_type(self): """ Expose the ContentType that the child relates to. This can be used for the ``polymorphic_ctype`` field. """ return ContentType.objects.get_for_model(self.model, for_concrete_model=False) def get_formset_child(self, request, obj=None, **kwargs): # Similar to GenericInlineModelAdmin.get_formset(), # make sure the GFK is automatically excluded from the form defaults = {"ct_field": self.ct_field, "fk_field": self.ct_fk_field} defaults.update(kwargs) return super(GenericPolymorphicInlineModelAdmin.Child, self).get_formset_child( request, obj=obj, **defaults ) class GenericStackedPolymorphicInline(GenericPolymorphicInlineModelAdmin): """ The stacked layout for generic inlines. """ #: The default template to use. template = "admin/polymorphic/edit_inline/stacked.html"