form_for_instance and form argument, empty form?

7 views
Skip to first unread message

David Larlet

unread,
Nov 7, 2007, 8:22:03 AM11/7/07
to Django developers
Hi all,

I'd spent a long time finding that bug but I want to be sure before
submitting it on Trac. If you pass a form argument to form_for_instance
like that:

forms.form_for_instance(foo, form=FooForm)

with an instance of foo which only contains a basic field (let's say a
CharField) and FooForm with a unique field too, the input rendered will
not be completed with the content of the foo instance. If you remove the
form argument:

forms.form_for_instance(foo)

the generated form contains the content of the foo instance.

I'd tried to find the bug but this line (122 of newforms.models):

return type(opts.object_name + 'InstanceForm', (form,),
{'base_fields': base_fields, '_model': model,
'save': make_instance_save(instance, fields, 'changed')})

give me headaches ;-). The only thing I can say is that base_fields
contains the initial data before this line but my returned form not.

Did somebody use this argument and can confirm?

Regards,
David

Marty Alchin

unread,
Nov 7, 2007, 9:11:11 AM11/7/07
to django-d...@googlegroups.com
This should probably be asked on django-users, as it's more about how
to use form_for_instance() than any internal development. And yes,
that means I don't believe it's a bug, and I'll gladly explain more on
django-users so more people can hear it.

-Gul

David Larlet

unread,
Nov 7, 2007, 9:42:58 AM11/7/07
to django...@googlegroups.com, django-d...@googlegroups.com
I thought that it was more appropriated to post it on the devlist
because it sounds like a bug but ok let's move it on the userlist, sorry
for the noise here. I'll be glad to hear your solution.

David

Marty Alchin a écrit :

Marty Alchin

unread,
Nov 7, 2007, 10:30:25 AM11/7/07
to django-d...@googlegroups.com
> > On 11/7/07, David Larlet <lar...@gmail.com> wrote:
> >> Hi all,
> >>
> >> I'd spent a long time finding that bug but I want to be sure before
> >> submitting it on Trac. If you pass a form argument to form_for_instance
> >> like that:
> >>
> >> forms.form_for_instance(foo, form=FooForm)
> >>
> >> with an instance of foo which only contains a basic field (let's say a
> >> CharField) and FooForm with a unique field too, the input rendered will
> >> not be completed with the content of the foo instance. If you remove the
> >> form argument:
> >>
> >> forms.form_for_instance(foo)
> >>
> >> the generated form contains the content of the foo instance.
> >>
> >> I'd tried to find the bug but this line (122 of newforms.models):
> >>
> >> return type(opts.object_name + 'InstanceForm', (form,),
> >> {'base_fields': base_fields, '_model': model,
> >> 'save': make_instance_save(instance, fields, 'changed')})
> >>
> >> give me headaches ;-). The only thing I can say is that base_fields
> >> contains the initial data before this line but my returned form not.
> >>
> >> Did somebody use this argument and can confirm?

I can confirm that it does in fact work as you describe, but I don't
believe it's a bug. The only documentation I was able to find on this
argument is in the docstrings for form_for_model and
form_for_instance, both of which clearly state that it's intended to
receive a subclass of BaseForm, not Form.

Here's the difference.

BaseForm is what provides all the real functionality for the form:
iterating over fields, triggering HTML output, managing validation,
etc. It's not normally used outside of Django's internals, because the
Form class subclasses it already. However, it doesn't know anything
about how to get the fields for the form. Instead, the Form class uses
a special way to pull fields out of the class definition and assign
them where they belong. form_for_model and form_for_instance don't
need this syntax, so they bypass that step by directly assigning the
fields where they belong when they create the class.

When you pass in a subclass of Form, it's already got its fields in
the right place, but more importantly, it triggers that syntax
checking again, where it looks for new fields. It basically copies
fields from a parent class, then adds in the fields that it found in
the new class. However, since form_for_model and form_for_instance
don't supply any fields that way, all it gives the new form is what it
found in the parent class, completely overwriting the fields it got
from form_for_*.

That was a bit of a long explanation, but I think it's necessary to
know what's going on, and it leaves me with two more points.

As for what that form argument was meant for, it's designed for a
BaseForm subclass. This allows you to add *methods* to your generated
form, not new fields.

As for what you're trying to do, check out the 'fields' argument to
form_for_instance. It creates a form with just a subset of the fields
from the original model.

-Gul

David Larlet

unread,
Nov 7, 2007, 1:12:58 PM11/7/07
to django-d...@googlegroups.com
Marty Alchin a écrit :

>>> On 11/7/07, David Larlet <lar...@gmail.com> wrote:
>>>
>>>> Hi all,
>>>>
>>>> I'd spent a long time finding that bug but I want to be sure before
>>>> submitting it on Trac. If you pass a form argument to form_for_instance
>>>> like that:
>>>>
>>>> forms.form_for_instance(foo, form=FooForm)
>>>>
>>>> with an instance of foo which only contains a basic field (let's say a
>>>> CharField) and FooForm with a unique field too, the input rendered will
>>>> not be completed with the content of the foo instance. If you remove the
>>>> form argument:
>>>>
>>>> forms.form_for_instance(foo)
>>>>
>>>> the generated form contains the content of the foo instance.
>>>>
>>>> I'd tried to find the bug but this line (122 of newforms.models):
>>>>
>>>> return type(opts.object_name + 'InstanceForm', (form,),
>>>> {'base_fields': base_fields, '_model': model,
>>>> 'save': make_instance_save(instance, fields, 'changed')})
>>>>
>>>> give me headaches ;-). The only thing I can say is that base_fields
>>>> contains the initial data before this line but my returned form not.
>>>>
>>>> Did somebody use this argument and can confirm?
>>>>
>
> I can confirm that it does in fact work as you describe, but I don't
> believe it's a bug. The only documentation I was able to find on this
> argument is in the docstrings for form_for_model and
> form_for_instance, both of which clearly state that it's intended to
> receive a subclass of BaseForm, not Form.
>
You're absolutely right on that point. I just found it in the
documentation too:
http://www.djangoproject.com/documentation/models/model_forms/
Wow, thanks for this interesting explanation! I always wondered what's
the difference between Form and BaseForm without digging too deep into
the code. Now that's really clear.

Unfortunately, what happens with my Form is exactly what I'd like to do:
create a generic form from a Form class which define fields (with
widgets, etc) and methods and which is initialized with the content of
the instance. That seemed more natural to me instead of dealing with
fields, form and eventually form_for_fields... I know that those
functions are just shortcuts but it works very well in terms of
genericity and my main concern here is to integrate a generic way to
handle forms for resources in restapi.

If you have any suggestion about that I'll be very happy to learn a bit
more :)

David

Marty Alchin

unread,
Nov 7, 2007, 1:39:05 PM11/7/07
to django-d...@googlegroups.com

Well, from what you posted before, your forms aren't really all that
"generic", so I'm not sure exactly what you're trying to accomplish.
If you're just looking for a simple way to populate a form's initial
values with those from a matching model, try this:

foo = Foo.objects.get(id=1)
form = FooForm(initial=foo.__dict__)

Now, I haven't tested that, so I make no guarantees, but at a quick
glance, it looks like it should do what you're looking for. Remember,
form_for_instance is for creating a form *and* populating it, not just
for populating an existing form. Also, keep in mind that you won't
have the save() method on FooForm unless you add it yourself, so I'm
really not sure how this would be any better than just using
form_for_instance as-is.

-Gul

SmileyChris

unread,
Nov 7, 2007, 7:09:02 PM11/7/07
to Django developers
On Nov 8, 4:30 am, "Marty Alchin" <gulop...@gamemusic.org> wrote:
> When you pass in a subclass of Form, it's already got its fields in
> the right place, but more importantly, it triggers that syntax
> checking again, where it looks for new fields. It basically copies
> fields from a parent class, then adds in the fields that it found in
> the new class. However, since form_for_model and form_for_instance
> don't supply any fields that way, all it gives the new form is what it
> found in the parent class, completely overwriting the fields it got
> from form_for_*.

Actually, I see no good reason why you shouldn't be able to use a Form
as the SuperClass of your form_for_* form.

I've just attached a patch to #3815 which makes it work.

Marty Alchin

unread,
Nov 7, 2007, 8:40:07 PM11/7/07
to django-d...@googlegroups.com
On 11/7/07, SmileyChris <smile...@gmail.com> wrote:
> Actually, I see no good reason why you shouldn't be able to use a Form
> as the SuperClass of your form_for_* form.

Yeah, I was trying avoid the issue of "should" of "shouldn't" and just
point out what was going on.

-Gul

David Larlet

unread,
Nov 8, 2007, 5:07:28 AM11/8/07
to django-d...@googlegroups.com
Marty Alchin a écrit :

> On 11/7/07, David Larlet <lar...@gmail.com> wrote:
>
>> Unfortunately, what happens with my Form is exactly what I'd like to do:
>> create a generic form from a Form class which define fields (with
>> widgets, etc) and methods and which is initialized with the content of
>> the instance. That seemed more natural to me instead of dealing with
>> fields, form and eventually form_for_fields... I know that those
>> functions are just shortcuts but it works very well in terms of
>> genericity and my main concern here is to integrate a generic way to
>> handle forms for resources in restapi.
>>
>
> Well, from what you posted before, your forms aren't really all that
> "generic", so I'm not sure exactly what you're trying to accomplish.
>
Ok, my previous example is really basic to extract the issue but I need
to explain the background now.

restapi [1] let you define resources which match a queryset. Those
resources are python class (just read at the 5 steps on the homepage of
the project to understand it well). When a request is done to the url,
the class call the __call__ function and dispatch this one to the right
function given the HTTP verb (to sum up).

Now let's focus on the form part. The python class (Collection) can be
instantiated with a form_class argument which is used in all functions
that require form_for_* and this is a key element here. You can have a
look at the bottom of the responder file [2] to see what I mean. I
didn't know that a BaseForm was required here when I'd proposed to add
this argument [3] and I thought that it was a good way to have a generic
form which can be customized.

Since your previous email, I'm a bit confused about that because I
realize that it's a bit useless if I can't get my forms from instances
rendered with instances' data. I hope this is more clear now (even if my
english is not that good...).

[1] http://code.google.com/p/django-rest-interface/
[2]
http://django-rest-interface.googlecode.com/svn/trunk/django_restapi/responder.py
[3]
http://groups.google.com/group/django-developers/browse_thread/thread/f86bd8ea6579baaa/709fdf8e092084ea?lnk=gst

SmileyChris a écrit :


> On Nov 8, 4:30 am, "Marty Alchin" <gulop...@gamemusic.org> wrote:
>

>> When you pass in a subclass of Form, it's already got its fields in
>> the right place, but more importantly, it triggers that syntax
>> checking again, where it looks for new fields. It basically copies
>> fields from a parent class, then adds in the fields that it found in
>> the new class. However, since form_for_model and form_for_instance
>> don't supply any fields that way, all it gives the new form is what it
>> found in the parent class, completely overwriting the fields it got
>> from form_for_*.
>>
>

> Actually, I see no good reason why you shouldn't be able to use a Form
> as the SuperClass of your form_for_* form.
>

> I've just attached a patch to #3815 which makes it work.
>
>

Did this patch populate form fields with instance data? I can't figure
out this given the tests.

Regards,
David

PS: RajeshD let me know if you're on this list, otherwise I'll forward
this email to you, I don't want to cross post anymore.

Reply all
Reply to author
Forward
0 new messages