Yes, I am defining _construct_form() as you do. I had just not thought to set initial there - but that makes sense. For reference, here is my current code, with it setting initial in _construct_form. But you may want to read on below before bothering to look at it because I have a theory about why cleaned_data is not getting set.
class TaskTileDetailCustomBaseFormSetForCreate(BaseFormSet): def __init__(self, *args, **kwargs): self.tiles = list(kwargs.pop("tiles")) self.extra = len(self.tiles) super(TaskTileDetailCustomBaseFormSetForCreate, self).__init__ (*args, **kwargs) def _construct_form(self, i, **kwargs): kwargs["tile"] = self.tiles[i] kwargs["initial"] = {'tile':self.tiles[i].id} return super(TaskTileDetailCustomBaseFormSetForCreate, self)._construct_form(i, **kwargs) def makeTaskTileDetailFormSetForCreate(tileList, data=None): TaskTileDetailFormSet = formset_factory(form=TaskTileDetailForm, formset=TaskTileDetailCustomBaseFormSetForCreate) taskTileDetailFormSet = TaskTileDetailFormSet(data, tiles=tileList) return taskTileDetailFormSet After debugging into the source some more, I'm finding that my the problem is related to the fact that empty_permitted is getting set to True when all of my forms are considered "extra. Specifically, in site- packages/django/forms/forms.py, in the full_clean() function, I find that when empty_permitted is set to True, this code executes, which drops me out of the code without setting cleaned_data at all: # If the form is permitted to be empty, and none of the form data has # changed from the initial data, short circuit any validation. if self.empty_permitted and not self.has_changed(): return It seems to me that if I pass in initial when I create the TaskTileDetailFormSet, ie, like this: initial = [] for tile in tileList: initial.append({'tile': tile.id}) taskTileDetailFormSet = TaskTileDetailFormSet(data, tiles=tileList, initial=initial) then empty_permitted gets set to False ibecause my initial_form_count is non-zero. IE, in site-packages/django/forms/formsets.py, in _construct_form(), in thh code that looks like this: # Allow extra forms to be empty. if i >= self._initial_form_count: defaults['empty_permitted'] = True But if I pass initial in via my own _construct_form() function as you suggested, then I have no initial data, so all of my forms are "extra". IN this case self._initial_form_count is 0, and it seems that the result is that cleaned_data doesn't get set correctly. I am probably far from undrestanding this, but if what I said is atually true, it seems like this is actually a bug? The bug being that cleaned_data is not getting set correctly when the forms are created as "extra" forms. Perhaps cleaned_data is not supposed to get set in this case? The whole reason that I happened upon this is beacuse I am trying to identify which form is which, so I was looking at cleaned_data['tile']. I set that myself anyway, so I can just look at data['tile']. However, it seems that none of my post data is getting put in cleaned_data, so that still seems like a general problem. Margie On Mar 3, 11:17 pm, Malcolm Tredinnick <malc...@pointy-stick.com> wrote: > On Tue, 2009-03-03 at 22:42 -0800, Margie wrote: > > Hi Malcolm - Sorry, another formset question. Maybe you'll know the > > answer offhand, have been trying to figure this out for awhile now. > > I'm trying to set the initial value for a form field inside the > > constructor for the form. The high level goal is to send out the > > 'tile' field as a hidden input so that I can get it back in the POST > > so that I can figure out wich 'tile' each form in the forset > > corresonds to. > > > For example, I'm trying to do this in my form constructor > > (specifically see the ==> line) > > > class TaskTileDetailForm(forms.ModelForm): > > class Meta: > > model=TaskTileDetail > > exclude=('task') > > If this is a cut-and-paste, this line is almost certainly a bug. The > "exclude" parameter should be a tuple or a list, so ("task",) or > ["task"] (note the trailing comma in the tuple case). > > > > > def __init__(self, tile, *args, **kwargs): > > This line is a probably going to be a problem at some point. You cannot > just add new positional arguments into the front of the __init__ call > like this. When Django calls the constructor, it could well be passing > the "data" parameter as the first argument. > > Instead, pass it in as a keyword argument. To wit: > > def __init__(self, *args, **kwargs): > tile = kwargs.pop("tile") > super(TaskTileDetailForm, self).__init__(*args, **kwargs) > ... > > That preserves the ordering of positional arguments. > > > super(TaskTileDetailForm, self).__init__(*args, **kwargs) > > self.fields['tile'].widget = forms.HiddenInput() > > ==> self.fields['tile'].initial = tile.id > > > If I do this, then later when processing my POST data there is no > > 'tile' key in cleaned_data. IE, I get a KeyError when I do this: > > if taskTileDetailForm.cleaned_data["tile"] in taskTiles: > > > If I intiaizize it with the initial arg when creating the formset, the > > cleaned_data["tile"] key is set just fine, ie when I use initial as > > shown at the ==> below: > > It seems like the problem is how is "tile" meant to be known to each > form? When a formset is constructed, it just constructs and essentially > normal form, passing in any "data" and "initial" parameters. If you need > to do any extra setup on the form -- particularly dealing with passing > in extra parameters -- then you have to do something similar to what I > did in my blog post ([1], for those who don't know what we're talking > about). > > So, either, pass in "initial" to the formset, or override the > _construct_form() method, as in the blog post. That's precisely why I > had to do it that way in my example: I needed to pass in some custom > information to each form as it was constructed as part of the formset. > > The difference between your case and mine, is that your extra data > corresponds directly to a form field (in mine, the question text was > auxiliary data that wasn't a form field). So I would probably set it up > using the "initial" parameter, if it was me. It would involve far less > formset base class customisation. > > [1]http://www.pointy-stick.com/blog/2009/01/23/advanced-formset-usage-dj... > > > def makeTaskTileDetailFormSetForCreate(tileList, data=None): > > TaskTileDetailFormSet = formset_factory > > (form=TaskTileDetailForm, formset=CustomBaseFormSe) > > > initial = [] > > for tile in tileList: > > initial.append({'tile': tile.id}) > > Since this is a read-only attribute, you could probably just write > > initial = [{"tile": tile.id}] * len(tileList) > > That will make each element of initial a reference to the same > dictionary, but that's not a problem since the initial data is only > read, never changed. > > Regards, > Malcolm --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Django users" group. To post to this group, send email to django-users@googlegroups.com To unsubscribe from this group, send email to django-users+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/django-users?hl=en -~----------~----~----~----~------~----~------~--~---