ModelForm: Dynamically change model and fields

1,325 views
Skip to first unread message

Dominik Szopa

unread,
Feb 29, 2008, 3:05:15 AM2/29/08
to Django users
Hello,

I'm trying to override init method of ModelForm class, i want
dynamically change model and fields for ModelForm class. I have tried
following:

class Form(ModelForm):
class Meta:
model = None
fields = ('',)

def __init__(self, model, fields, *args, **kwargs):
self._meta.fields = fields
self._meta.model = model
super(Form, self).__init__(*args, **kwargs)

But this doesn't work, how to pass new _meta.fields and _meta.model in
init method ???

----
Dominik Szopa

James Bennett

unread,
Feb 29, 2008, 3:17:02 AM2/29/08
to django...@googlegroups.com
On Fri, Feb 29, 2008 at 2:05 AM, Dominik Szopa <dsz...@gmail.com> wrote:
> I'm trying to override init method of ModelForm class, i want
> dynamically change model and fields for ModelForm class. I have tried
> following:

This is pretty cumbersome and almost certainly not the best way to go
about what you want.

Go have a look at the code for the old
form_for_model/form_for_instance helpers; don't try to use them
directly, but read through their code to see how they dynamically
build and return a new form class, because that's what you want to do
in your own code.


--
"Bureaucrat Conrad, you are technically correct -- the best kind of correct."

brian

unread,
Feb 29, 2008, 3:28:35 AM2/29/08
to django...@googlegroups.com
> But this doesn't work, how to pass new _meta.fields and _meta.model in
> init method ???

I am a bit concerned that your use-case for this kind of functionality
is not well justified. However, you cannot change the fields in the
__init__ method of the form. By the time __init__ is executed the form
has already assembled the fields. This is done with the use of a
metaclass that dictates how your Form class is built. You need to do
some reading on metaclasses (which is an advanced Python topic) before
you can accomplish something like this. There can be ways to simply
adjust the values assigned to the class inside the class since the
class defintion can live anywhere, but it would be wise to understand
how all that works.

Again, I would seriously reconsider your approach and make sure that
this type of functionality is critical to your app.

Here are some handy links:

http://docs.python.org/ref/metaclasses.html
http://en.wikipedia.org/wiki/Metaclass
http://code.djangoproject.com/browser/django/trunk/django/newforms/models.py#L215

Pigletto

unread,
Feb 29, 2008, 3:36:20 AM2/29/08
to Django users
> I am a bit concerned that your use-case for this kind of functionality
> is not well justified. However, you cannot change the fields in the
> __init__ method of the form. By the time __init__ is executed the form
> has already assembled the fields. This is done with the use of a
> metaclass that dictates how your Form class is built.
I don't know the use case either but I don't agree that you can't
change fields in init.
This works:

def __init__(self, *args, **kwargs):
super(Form, self).__init__(*args, **kwargs)
self.fields['my_new_field'] = forms.CharField(....)
self.fields['myoldfield'].widget = WidgetX(...)
# to reorder fields use:
self.fields.keyOrder = ['field1', 'field2',...]
etc.

--
Maciej Wisniowski

Dominik Szopa

unread,
Feb 29, 2008, 2:47:35 PM2/29/08
to Django users

johan de taeye

unread,
Mar 1, 2008, 3:48:47 AM3/1/08
to Django users

This approach works for me in a similar situation:
import new
UploadMeta = new.classobj("UploadMeta", (), {
'model': yourModel,
'fields': ('yourfield1', 'yourfield2')
})
UploadForm = new.classobj("UploadForm", (ModelForm,), {
"Meta": UploadMeta
})

In case you're wondering about the practical use case of this...
I use this to read a CSV-formatted file and validate the input rows
one by one before saving to the database.
So, when reading the header line of the file, I dynamically create the
form: the column names in the first row define the fields. For every
following line in the input file I then process it with (simplified):
try:
form = UploadForm(dictionary_built_from_the_row)
form.save()
except:
... process validation errors

My 2 cents: similar use cases for a more dynamic use of ModelForm are
sure around. It would be great if they would be supported in a cleaner
and more intuitive way than currently available.

Johan


On Feb 29, 8:47 pm, Dominik Szopa <dsz...@gmail.com> wrote:
> On 29 Lut, 09:28, brian <bros...@gmail.com> wrote:
>
>
>
>
>
> > > But this doesn't work, how to pass new _meta.fieldsand _meta.modelin
> > > init method ???
>
> > I am a bit concerned that your use-case for this kind of functionality
> > is not well justified. However, you cannotchangethefieldsin the
> > __init__ method of the form. By the time __init__ is executed the form
> > has already assembled thefields. This is done with the use of a
> > metaclass that dictates how your Form class is built. You need to do
> > some reading on metaclasses (which is an advanced Python topic) before
> > you can accomplish something like this. There can be ways to simply
> > adjust the values assigned to the class inside the class since the
> > class defintion can live anywhere, but it would be wise to understand
> > how all that works.
>
> > Again, I would seriously reconsider your approach and make sure that
> > this type of functionality is critical to your app.
>
> > Here are some handy links:
>
> >http://docs.python.org/ref/metaclasses.htmlhttp://en.wikipedia.org/wi......
>
> Thank you, i'll try to read about metaclasses, thanks for the links :)- Hide quoted text -
>
> - Show quoted text -

Malcolm Tredinnick

unread,
Mar 1, 2008, 4:09:35 AM3/1/08
to django...@googlegroups.com

On Sat, 2008-03-01 at 00:48 -0800, johan de taeye wrote:
>
> This approach works for me in a similar situation:
> import new
> UploadMeta = new.classobj("UploadMeta", (), {
> 'model': yourModel,
> 'fields': ('yourfield1', 'yourfield2')
> })
> UploadForm = new.classobj("UploadForm", (ModelForm,), {
> "Meta": UploadMeta
> })
>
[...]

> My 2 cents: similar use cases for a more dynamic use of ModelForm are
> sure around. It would be great if they would be supported in a cleaner
> and more intuitive way than currently available.

It's two lines of code! How could it be cleaner??? We moved to a class
style here precisely so that we wouldn't have to keep extending and so
that people could do exactly what you're doing.

There are any number of different things people are going to want to do
with forms. We cannot and should not attempt to cover them all because
Python already support dynamic class creation and you should use that.
ModelForms provide a class structure for one case that is common for
some people: that of mapping a model directly to a bunch of form fields.
It allows some customisation via field ordering and specifying what to
leave out, as well as providing an easy way to add extra methods.

If you want dynamic adjustments, do something similar to what you're
doing and use Python's native support for dynamic class creation. You
might decide you pattern is common enough in your code that it becomes a
helper function. Then you write a helper function and it's cool.

As a side note, your code contains a slight bug. The "new" module
carries some historical baggage and part of that is that new.classobj
only provides you with an old-style class (not one with "object" as a
base). Since newforms are based around new-style classes, it would be
more accurate (and shorter) to create the class using type(). The
differences are subtle, but new-style classes contain a few more methods
than old-style ones and something might well be relying on that.
Normally, it's not an issue, since you inherit from Form when creating
classes in code. But if you're doing it dynamically, you should use the
right object creation function.

Regards,
Malcolm

--
Everything is _not_ based on faith... take my word for it.
http://www.pointy-stick.com/blog/

eraoul

unread,
Mar 1, 2008, 11:09:18 AM3/1/08
to Django users
I'm not sure if this thread is the right place to post, but let me ask
by sort of related question anyway:

I have a form that I need to display, where one of the fields can
appear one or many times. The user is able to dynamically add extra
copies of an input control to the web page by clicking a button --
sort of like in gmail, for instance, where you can add additional
attachments to an email message.

This seems like pretty standard stuff these days. Is there a best-
practices way to have the django form adapt to the new controls?

I don't want to reinvent the wheel here -- can anyone point me to a
case where this has already been done?

thanks!
eric
Reply all
Reply to author
Forward
0 new messages