6

I just want to know how can I set initial values to empty_form.

I do create the Inlines with initial values for extra forms without problem, but, when user clicks to Add button, the fields I expect it have the initial values show up empty, and I hope it have the same initial values than extra forms.

How could I make the empty_form to be filled with initial data?

Thanks in advance.

5 Answers 5

2

Django doesn't really provide a way to set initial values for empty forms. I've found a couple ways to work around this:

  1. Set the field values dynamically in javascript.
  2. Overwrite the empty_form property for your formset.

example:

formset = formset_factory(MyClass, **kwargs)
empty = formset.empty_form    
# empty is a form instance, so you can do whatever you want to it
my_empty_form_init(empty_form)
formset.empty_form = empty_form
Sign up to request clarification or add additional context in comments.

1 Comment

Can't set attribute on line 4: formset.empty_form = empty_form
1

I had a similar problem and what finally worked for me was using Django Dynamic Formset. What DDF does is instead of using the empty form to create the new formset, it uses one of the extra_forms as a template. The default behavior is to clear all field values from the extra_form before inserting the HTML to the DOM, but you can use the keepFieldValues setting to specify the ones you want to keep.

In my case I wanted to keep all hidden field values:

$(function() {
    $('#myForm_table tbody tr').formset({
        keepFieldValues: 'input:hidden',
        }
    });
});

Of course you can bypass Django Dynamic Formsets and implement your own add/delete code with Javascript if you prefer.

Comments

1

Accepted answer didn't work for me, hopefully this will help someone in the future, this is my solution:

  1. Create a new class based on BaseInlineFormSet
  2. Override empty_form
  3. Create a FormSet with inlineformset_factory(formset=YourBaseInlineFormSet)
  4. Create a formset instance and pass parameters to initial on the formset instance
  5. Add the field on the HTML as usual

I used BaseInlineFormSet, but probably will work with other types of FormSet

verification is the name of the field for my example.

forms.py

class YourBaseInlineFormSet(forms.BaseInlineFormSet):
    @property
    def empty_form(self):  # This is almost the same as Django 3.1 code
        form = self.form(
            auto_id=self.auto_id,
            prefix=self.add_prefix("__prefix__"),
            empty_permitted=True,
            use_required_attribute=False,
            initial={"verification": self.initial_extra[0]["verification"]},  # This is the extra parameter
            **self.get_form_kwargs(None),
        )
        self.add_fields(form, None)
        return form


YourFormSet = forms.inlineformset_factory(
    SomeObject,
    SomeRelatedObject,
    fields="__all__",
    widgets={"verification": forms.HiddenInput},
    formset=YourBaseInlineFormSet,
)

views.py

from .forms import YourFormSet

def your_view(request):
    formset = YourFormSet(
        data=request.POST or None,
        instance=object,
        queryset=object.related_objects.all()
        initial=[{"verification": verification} for a in range(FormSet().total_form_count())],
    )
    return render(request, template, context={'formset': formset})

template.html

<div id="empty_form" style="display:none">
    {{ formset.empty_form }}
</div>

Working on Django 3.1

Comments

0

There is at least one way to do this: Specify the default value on your model Field.

Of course, this may have side effects, depending on your implementation of the model.

1 Comment

Even with the initial values defined in my decimal field with default value set in the model class, the field in the new (added) row shows blank.
0

As @jkk-jonah mentioned, BaseFormSet does not provide a way to set initial values in the empty_form. However, a small change can provide a simple solution.

The following provides a way to supply the FormSet instance with empty initial values without disrupting its base behavior.

from django.forms.formsets import BaseFormSet
class FormSetWithDefaultEmptyFormInitials(BaseFormSet):
    """This formset enables you to set the initial values in ``empty_form``.

    Usage: ``formset_factory(..., formset=FormSetWithDefaultEmptyFormInitials)``
    """

    def __init__(self, *args, **kwargs):
        if 'empty_initial' in kwargs:
            self._empty_initial = kwargs.pop('empty_initial')
        super().__init__(*args, **kwargs)

    def get_form_kwargs(self, index):
        """Augmented to return the empty initial data
        when the index is ``None``,
        which is the case when creating ``empty_form``.
        """
        if index is None:
            kwargs = self.form_kwargs.copy()
            if self._empty_initial:
                # Assign the initial value passed to the Form class.
                kwargs['initial'] = self._empty_initial
        else:
            kwargs = super().get_form_kwargs(index)
        return kwargs

Then to use this you'd do something like:

NonEmptyFormSet = formset_factory(
    BringYourOwnForm,
    min_num=1,
    extra=1,
    formset=FormSetWithDefaultEmptyFormInitials,
)
# Let's say your form has name and address fields...
empty_form_initial_values = {'name': 'default name', 'address': 'default address'}
formset = NonEmptyFormSet(empty_initial=empty_form_initial_values)
asset formset.empty_form.initial == empty_form_initial_values

In my implementation empty_form is used to provide a template for frontend javascript to add additional forms to the formset. Thus, this allows me to set the initial values for that all of the forms in that formset.

Note, this does not take the place of initial values to the minimum number of forms within the formset (e.g. formset_factory(min_num=2, ...)). Therefore, it is necessary to assign those through the standard initial keyword argument.

Tested with Django 3.2.

See also the standard implementation of get_form_kwargs.

This partially extends the answer given by @RobertPro. Or at least, I used their answer as the stepping stone to my own solution.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.