Skip to content Skip to sidebar Skip to footer

Accessing Parent Model Instance From Modelform Of Admin Inline

I'm using a TabularInline in Django's admin, configured to show one extra blank form. class MyChildInline(admin.TabularInline): model = MyChildModel form = MyChildInlineFor

Solution 1:

Update: As of Django 1.9, there is a def get_form_kwargs(self, index) method in the BaseFormSet class. Hence, overriding that passes the data to the form.

This would be the Python 3 / Django 1.9+ version:

classMyFormSet(BaseInlineFormSet):defget_form_kwargs(self, index):
        kwargs = super().get_form_kwargs(index)
        kwargs['parent_object'] = self.instance
        return kwargs


classMyForm(forms.ModelForm):def__init__(self, *args, parent_object, **kwargs):
        self.parent_object = parent_object
        super(MyForm, self).__init__(*args, **kwargs)


classMyChildInline(admin.TabularInline):
    formset = MyFormSet
    form = MyForm

For Django 1.8 and below:

To pass a value of a formset to the individual forms, you'd have to see how they are constructed. An editor/IDE with "jump to definition" really helps here to dive into the ModelAdmin code, and learn about the inlineformset_factory and it's BaseInlineFormSet class.

From there you'll find that the form is constructed in _construct_form() and you can override that to pass extra parameters. It will likely look something like this:

classMyFormSet(BaseInlineFormSet):
    def_construct_form(self, i, **kwargs):
        kwargs['parent_object'] = self.instance
        returnsuper(MyFormSet, self)._construct_form(i, **kwargs)

    @propertydefempty_form(self):
        form = self.form(
            auto_id=self.auto_id,
            prefix=self.add_prefix('__prefix__'),
            empty_permitted=True,
            parent_object=self.instance,
        )
        self.add_fields(form, None)
        return form

classMyForm(forms.ModelForm):
    def__init__(self, *args, **kwargs):
        self.parent_object = kwargs.pop('parent_object', None)
        super(MyForm, self).__init__(*args, **kwargs)


classMyChildInline(admin.TabularInline):
    formset = MyFormSet
    form = MyForm

Yes, this involves a private _construct_form function.

update Note: This doesn't cover the empty_form, hence your form code needs to accept the parameters optionally.

Solution 2:

I'm using Django 1.10 and it works for me: Create a FormSet and put the parent object into kwargs:

classMyFormSet(BaseInlineFormSet):defget_form_kwargs(self, index):
        kwargs = super(MyFormSet, self).get_form_kwargs(index)
        kwargs.update({'parent': self.instance})
        return kwargs

Create a Form and pop an atribute before super called

classMyForm(forms.ModelForm):def__init__(self, *args, **kwargs):
        parent = kwargs.pop('parent')
        super(MyForm, self).__init__(*args, **kwargs)
        # do whatever you need to with parent

Put that in the inline admin:

classMyModelInline(admin.StackedInline):
    model = MyModelfields= ('my_fields', )
    form = MyFromformset= MyFormSet

Solution 3:

AdminModel has some methods like get_formsets. It receives an object and returns a bunch of formsets. I think you can add some info about parent object to that formset classes and use it later in formset's __init__

Solution 4:

Expanding on ilvar's answer a bit, If the form field of interest is constructed from a model field, you can use the following construction to apply custom behavior to it:

classMyChildInline(admin.TabularInline):
    model = MyChildModel
    extra = 1defget_formset(self, request, parent=None, **kwargs):
        defformfield_callback(db_field):
            """
            Constructor of the formfield given the model field.
            """
            formfield = self.formfield_for_dbfield(db_field, request=request)
            if db_field.name == 'my_choice_field'and parent isnotNone:
                formfield.choices = parent.get_choices()
            return formfield
        returnsuper(MyChildInline, self).get_formset(
            request, obj=obj, formfield_callback=formfield_callback, **kwargs)
        return result

Post a Comment for "Accessing Parent Model Instance From Modelform Of Admin Inline"