ModelForm initial= vs. instance=

4,412 views
Skip to first unread message

David Durham, Jr.

unread,
Sep 19, 2008, 9:54:31 AM9/19/08
to django...@googlegroups.com
I'm writing a little wizard application to walk a user through
creating a bunch of Model objects. I stumbled across something kind
of interesting; if I create a ModelForm like so:

SomeModelForm(initial={...})

All of the fields I have are prepopulated, except the ForeignKey ones.
I don't have many-to-many, so can't say if the problem is there too.
Now, if I create one with:

SomeModelForm(instance=some_instance)

All fields are prepopulated correctly, including the ForeignKey
variety. To me, this is not desirable because I have a component that
now has to check if it has a ModelForm or not. Ideally I could pass a
{} with foreign key values and have drop-downs preselected with the
corresponding options.

Thinking about it a little more, couldn't this interface simply be:

ModelForm(initial=X)

where X is either a dict or a Model instance? Seems like this would
be consistent with the Form init (but Form would only work with {})
while adding what's needed for ModelForm.

Any thoughts?

Thanks,
Dave

Malcolm Tredinnick

unread,
Sep 19, 2008, 9:21:42 PM9/19/08
to django...@googlegroups.com

On Fri, 2008-09-19 at 08:54 -0500, David Durham, Jr. wrote:
> I'm writing a little wizard application to walk a user through
> creating a bunch of Model objects. I stumbled across something kind
> of interesting; if I create a ModelForm like so:
>
> SomeModelForm(initial={...})
>
> All of the fields I have are prepopulated, except the ForeignKey ones.
> I don't have many-to-many, so can't say if the problem is there too.
> Now, if I create one with:
>
> SomeModelForm(instance=some_instance)
>
> All fields are prepopulated correctly, including the ForeignKey
> variety. To me, this is not desirable because I have a component that
> now has to check if it has a ModelForm or not. Ideally I could pass a
> {} with foreign key values and have drop-downs preselected with the
> corresponding options.
>
> Thinking about it a little more, couldn't this interface simply be:
>
> ModelForm(initial=X)
>
> where X is either a dict or a Model instance?

No, there's quite a difference. "Initial" is used to set the initial
values and is quite appropriate when a *new* model instance is going to
be created. The "instance" is used when you want the ModelForm to be
used to update an existing instance of a model. Since Django does not
currently incorporate mind-reading technology, if the two parameters
were merged into one, the framework wouldn't know whether it's meant to
be updating an existing instance or creating a new one.

Regards,
Malcolm

David Durham, Jr.

unread,
Sep 20, 2008, 9:06:26 AM9/20/08
to django...@googlegroups.com
> No, there's quite a difference. "Initial" is used to set the initial
> values and is quite appropriate when a *new* model instance is going to
> be created. The "instance" is used when you want the ModelForm to be
> used to update an existing instance of a model.
> Since Django does not
> currently incorporate mind-reading technology, if the two parameters
> were merged into one, the framework wouldn't know whether it's meant to
> be updating an existing instance or creating a new one.

I don't think this is necessarily the case, and maybe you'll correct
me if I'm wrong. :) Looking at db.models.base.save_base, there is
some checking to see if 1) the pk is set and 2) if the pk exists in
the db when determining whether or not to save or update. And it does
not look as though ModelForm will force_update or force_insert when
calling instance.save().

Still though, couldn't your logic be applied on just an initial
variable. I.e., if initial is a dict, it's an insert. If it's a
model, then we have an update (not that I agree this is the way it
should be). It does however look as though initial values will
override instance values, and I don't see a way to retain this feature
without having both of these arguments. I guess I question how useful
the "initial values override instance values" feature is, because why
not simply change the model before constructing the form?

Anyway, none of this really has to do with my issue, which is that
initial= doesn't appear to set FK drop-downs (or I'm doing it wrong).
Using instance= does set FK drop-downs (even though this is not an
update; I create the model from a dict stored in a session), however I
am have trouble saving the values POSTed from a form created based on
the following sequence:

In a GET:

page_data = self._get_page_data(request.session, page0)
form_class = self.get_form_list(request.session)[page0]
if issubclass(form_class, forms.ModelForm):
form = form_class(instance=form_class.Meta.model(**page_data))
# ... render the form

When this form is POSTed, something like this happens:

if form.is_valid:
request.session[self._get_page_key(page0)] = form.cleaned_data


Basically, I store the cleaned_data in the session for POSTs and use
the cleaned_data in order to initialize a ModelForm for display after
GETs. Eventually, I have to save some things. When that happens, I
again get the data from a session:

for i in range(len(self.get_form_list(request.session))):
page_data = self._get_page_data(request.session, i)
form_class = form_list[i]
form = form_list[i](page_data)
form.save()

At this point, though, I will have trouble with foreign keys again.
Specifically, I have actual ForeignKey model objects in my data
argument, and save throws a TypeError like so:

int() argument must be a string or a number, not 'MyModel'

Thanks for any suggestions,
Dave

David Durham, Jr.

unread,
Sep 20, 2008, 7:18:28 PM9/20/08
to django...@googlegroups.com
> Basically, I store the cleaned_data in the session for POSTs and use
> the cleaned_data in order to initialize a ModelForm for display after
> GETs. Eventually, I have to save some things. When that happens, I
> again get the data from a session:
>
> for i in range(len(self.get_form_list(request.session))):
> page_data = self._get_page_data(request.session, i)
> form_class = form_list[i]
> form = form_list[i](page_data)
> form.save()
>
> At this point, though, I will have trouble with foreign keys again.
> Specifically, I have actual ForeignKey model objects in my data
> argument, and save throws a TypeError like so:
>
> int() argument must be a string or a number, not 'MyModel'


Just for the record, I ended up with this:

SomeModel(**page_data).save()

instead of the corresponding:

SomeModelForm(page_data).save()

and that resolves the issue with foreign keys. I guess ModelForm is
expecting ids instead of an actual model instance.

-Dave

Reply all
Reply to author
Forward
0 new messages