Question

Different initial data for each form in a Django formset

Is it possible to prepopulate a formset with different data for each row? I'd like to put some information in hidden fields from a previous view.

According to the docs you can only set initial across the board.

 45  27655  45
1 Jan 1970

Solution

 82

If you made the same mistake as me, you've slightly mistaken the documentation.

When I first saw this example...

formset = ArticleFormSet(initial=[
 {'title': 'Django is now open source',
  'pub_date': datetime.date.today(),}
])

I assumed that each form is given the same set of initial data based on a dictionary.

However, if you look carefully you'll see that the formset is actually being passed a list of dictionaries.

In order to set different initial values for each form in a formset then, you just need to pass a list of dictionaries containing the different data.

Formset = formset_factory(SomeForm, extra=len(some_objects)
some_formset = FormSet(initial=[{'id': x.id} for x in some_objects])
2014-05-06

Solution

 11

You need to use the technique described in this post in order to be able to pass parameters in. Credit to that author for an excellent post. You achieve this in several parts:

A form aware it is going to pick up additional parameters

Example from the linked question:

def __init__(self, *args, **kwargs):
    someeobject = kwargs.pop('someobject')
    super(ServiceForm, self).__init__(*args, **kwargs)
    self.fields["somefield"].queryset = ServiceOption.objects.filter(
                                                           somem2mrel=someobject)

Or you can replace the latter code with

    self.fields["somefield"].initial = someobject

Directly, and it works.

A curried form initialisation setup:

formset = formset_factory(Someform, extra=3)
formset.form = staticmethod(curry(someform, somem2mrel=someobject))

That gets you to passing custom form parameters. Now what you need is:

A generator to acquire your different initial parameters

I'm using this:

def ItemGenerator(Item):
    i = 0
    while i < len(Item):
        yield Item[i]
        i += 1

Now, I can do this:

iterdefs = ItemGenerator(ListofItems) # pass the different parameters 
                                      # as an object here
formset.form = staticmethod(curry(someform, somem2mrel=iterdefs.next()))

Hey presto. Each evaluation of the form method is being evaluated in parts passing in an iterated parameter. We can iterate over what we like, so I'm using that fact to iterate over a set of objects and pass the value of each one in as a different initial parameter.

2011-04-25