frank
frank

Reputation: 1342

Django inline model formset and inline model form handle initial differently

I'm using a StackedInline to populate a OneToOneField, but I want to make it optional, so I set min_num=0 and extra = 0.

I also want to include default values when the user presses the "➕ Add another" button.

class MyForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        kwargs['initial'] = {'hello': 'world'}

class MyInline(admin.StackedInline):
    form = MyForm
    extra = 0
    min_num=0

This works. When someone presses the "➕ Add another" button, the hello field is populated with world.

I also want to and do some custom validation, so it looks like I have to use BaseInlineFormSet. I moved the initial stuff to MyFormSet.__init__

class MyFormSet(BaseInlineFormSet):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        if not self.forms:
            return
        self.forms[0].fields['hello'].initial = 'world'
        
    def clean(self):
        # My custom validation

class MyInline(admin.StackedInline):
    formset = MyFormSet
    extra = 0
    min_num=0

But the initial values are no longer populated when the user presses the "➕ Add another" button, only when the form is initially displayed with extra = 1.

Is there another thing I need to do in MyFormSet to preserve the MyForm behavior?

Upvotes: 0

Views: 160

Answers (1)

tim-mccurrach
tim-mccurrach

Reputation: 6825

The problem

You can't try and set the initial property in the __init__ of your formset, since self.forms[0] doesn't even exist at that point. The correct place to do this is (as in your first) example, in the init method of your MyForm.

The solution

What you can do is use MyFormSet to implement your custom validation using clean, and use MyForm to add the logic about default values, then:

from django.forms import formset_factory

class MyInline(admin.StackedInline):
    form = Myform
    formset = MyFormSet
    extra = 0
    min_num=0

Upvotes: 1

Related Questions