Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing initial into Django's ModelFormSet

Tags:

I have a model formset that I need to pass the keyword initial in to (to have some initial values in the formset, such as date set to today).

As far as I can tell, the queryset argument (which defaults to YourModel.objects.all()) overrides this ability for initial empty forms to be created.

I specified the queryset as: YourModel.objects.none(), which prevents it from filling in the formset with entries from the database, but I still can't figure out how to pass initial in effectively.

Do I need to subclass a method from ModelForm?

like image 682
Vince Avatar asked Aug 21 '09 21:08

Vince


3 Answers

You are passing an empty queryset to ModelFormset.

ExampleModelFormSet = modelformset_factory(ExampleModel, extra=2)
formset = ExampleModelFormSet(queryset=ExampleModel.objects.none(),
                          initial=[{'name': 'Some Name'},
                                   {'name': 'Another Name'}])

which is indeed the way to achieve what you want. It makes sense to pass an empty queryset, else initial values will override the model's database value.

And if I suppose, you are passing a non-empty queryset, and yet want your extra forms to be filled will initial values, you can override the Formset.

from django.forms.models import modelformset_factory  
ExampleFormset = modelformset_factory(OpenIDProfile, extra=3)

class myFormset(ExampleFormset):

    def _construct_form(self, i, **kwargs):
        """
        Instantiates and returns the i-th form instance in a formset.
        """
        if self.is_bound and i < self.initial_form_count():
            pk_key = "%s-%s" % (self.add_prefix(i), self.model._meta.pk.name)
            pk = self.data[pk_key]
            pk_field = self.model._meta.pk
            pk = pk_field.get_db_prep_lookup('exact', pk)
            if isinstance(pk, list):
                pk = pk[0]
            kwargs['instance'] = self._existing_object(pk)
        if i < self.initial_form_count() and not kwargs.get('instance'):
            kwargs['instance'] = self.get_queryset()[i]

        defaults = {'auto_id': self.auto_id, 'prefix': self.add_prefix(i)}
        if self.data or self.files:
            defaults['data'] = self.data
            defaults['files'] = self.files
        # Check to confirm we are not overwriting the database value.
        if not i in range(self.initial_form_count()) and self.initial:
            try:
                defaults['initial'] = self.initial[i - self.initial_form_count()]
            except IndexError:
                pass
        # Allow extra forms to be empty.
        if i >= self.initial_form_count():
            defaults['empty_permitted'] = True
        defaults.update(kwargs)
        form = self.form(**defaults)
        self.add_fields(form, i)        
        return form

Note: initial is list of dicts. Its desirable to have items in list exactly equal to the number of extra.

like image 50
simplyharsh Avatar answered Oct 14 '22 04:10

simplyharsh


New in Django 1.4 ...if you set initial on a modelformset it now only applies to the 'extra' forms, not those bound to instances from the queryset:

https://docs.djangoproject.com/en/dev/topics/forms/modelforms/#providing-initial-values

So the various hacks above are no longer necessary and using initial is the answer.

like image 22
Anentropic Avatar answered Oct 14 '22 06:10

Anentropic


The best way I've found to do this is to just manually adjust the formset object's "extra" attribute after initialization. This has been tested and works in Django 1.9.

ExampleModelFormSet = modelformset_factory(ExampleModel, extra=1)
formset = ExampleModelFormSet(initial=[{'name': 'Some Name'},
                                       {'name': 'Another Name'}])
formset.extra = 3

This will spit out 3 forms (2 prepopulated and 1 blank).

If you want something more dynamic (e.g. initial is a var calculated somewhere else), you can do something like this:

ExampleModelFormSet = modelformset_factory(ExampleModel, extra=1)
formset = ExampleModelFormSet(initial=initial)
formset.extra += len(initial)

Hope that helps.

like image 33
Chad Avatar answered Oct 14 '22 06:10

Chad