add `form_kwargs` argument to `formset_factory` and `modelformset_factory`

The Formset class accepts a form_kwargs arguments that is subsequently given to the forms that are created. The ModelFormSet class accepts this argument and relays it to FormSet.

However, the factories do not take it as an input, and this is unfortunate.

I suggest to add it to the two “factories”.

Here is a possible use case : in the admin interface, a ModelAdmin.get_formset_kwargs() method allows to provide extra arguments to the formset in the “add / change” views (an example in the documentation shows how to pass request). However, it is impossible to provide arguments to the formset in the “changelist” view, because it is constructed using modelformset_factory in ModelAdmin.get_changelist_formset(). Even though this method takes arbitrary keywords arguments and relays them to the factory, the factory does not accept them.

Hmmm… This seems to be to be the wrong location for that.

The factories do not create formsets, and therefore don’t create forms, either.

They create a formset class, where creating an instance of that class creates the formset.

So it doesn’t appear appropriate to me to pass any form-specific parameters to the factory. (Especially since the admin is just one special-case use of formsets.)

Side note: In theory (if not in common practice) you can create multiple formsets from a single formset class, as in the case of a nested-formset situation.

For the situation you’re mentioning, the formset itself is not created in get_changelist_formset(). What is created there is the formset class. The instance of the formset (and the forms) are created in the changelist_view in the two separate locations (once each for GET and POST.

Looking at the GET portion of that view, you’ll find:

...
elif cl.list_editable and self.has_change_permission(request):
    FormSet = self.get_changelist_formset(request)
    formset = cl.formset = FormSet(queryset=cl.result_list)

The “POST” portion of this is:

FormSet = self.get_changelist_formset(request)
modified_objects = self._get_list_editable_queryset(
    request, FormSet.get_default_prefix()
)
formset = cl.formset = FormSet(
    request.POST, request.FILES, queryset=modified_objects
)

If you want to add additional parameters to the formset, you could override the changelist_view.

1 Like

Hi Ken,
Thank you very much for this clarification — indeed I was mistaken.
So, my beef with the default changelist_view is that it creates the formset in the way you have shown. Instead, I would have liked something like:

def get_changelist_formset_kwargs(self, request, queryset):
    kwargs = {'queryset': queryset}
    if request.method == 'POST':
        kwargs.update({'data': request.POST, 'files': request.FILES})
    return kwargs

def changelist_view(...):
    ...
    kwargs = self.get_changelist_formset_kwargs(request, queryset)
    formset = cl.formset = FormSet(**kwargs)
    ...

This would mimic what happens with the inline formsets (cf. ModelAdmin.get_formset_kwargs()), and would make it a bit easier to control the changelist formset… Do you think that this is something interesting?