Accessing parent model instance from modelform of admin inline

前端 未结 4 759
天命终不由人
天命终不由人 2020-12-24 07:59

I\'m using a TabularInline in Django\'s admin, configured to show one extra blank form.

class MyChildInline(admin.TabularInline):
    model = MyChildModel
           


        
4条回答
  •  甜味超标
    2020-12-24 08:30

    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:

    class MyFormSet(BaseInlineFormSet):
        def get_form_kwargs(self, index):
            kwargs = super().get_form_kwargs(index)
            kwargs['parent_object'] = self.instance
            return kwargs
    
    
    class MyForm(forms.ModelForm):
        def __init__(self, *args, parent_object, **kwargs):
            self.parent_object = parent_object
            super(MyForm, self).__init__(*args, **kwargs)
    
    
    class MyChildInline(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:

    class MyFormSet(BaseInlineFormSet):
        def _construct_form(self, i, **kwargs):
            kwargs['parent_object'] = self.instance
            return super(MyFormSet, self)._construct_form(i, **kwargs)
    
        @property
        def empty_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
    
    class MyForm(forms.ModelForm):
        def __init__(self, *args, **kwargs):
            self.parent_object = kwargs.pop('parent_object', None)
            super(MyForm, self).__init__(*args, **kwargs)
    
    
    class MyChildInline(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.

提交回复
热议问题