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
-~----------~----~----~----~------~----~------~--~---

Reply via email to