Move form_for_instance and form_for_model into django.db.models.Model?

5 views
Skip to first unread message

Todd O'Bryan

unread,
Jul 12, 2007, 1:38:44 PM7/12/07
to django-d...@googlegroups.com
Is there a good reason not to do something like the following in
django.db.models.Model?

def form(self):
return form_for_instance(self)

@classmethod
def form(cls):
return form_for_model(cls)

As I was writing this, I realized that you can't do this in Python
because you can't overload function names, but surely somebody smarter
than me can figure out a clever way that model_instance.form() and
ModelClass.form() would both do the right thing.

I mention this because I'm noticing that I want to customize forms and
think that the right OO place to do that is probably in the model. Being
able to override a standard function would make the right way to do it
pretty obvious.

I'm also pretty excited about not having another import for
form_for_model and form_for_instance in nearly all my views.py files.

What's more, this is completely backwards compatible!! :-)

Todd

Marty Alchin

unread,
Jul 12, 2007, 2:02:44 PM7/12/07
to django-d...@googlegroups.com
On 7/12/07, Todd O'Bryan <toddo...@mac.com> wrote:
> As I was writing this, I realized that you can't do this in Python
> because you can't overload function names, but surely somebody smarter
> than me can figure out a clever way that model_instance.form() and
> ModelClass.form() would both do the right thing.

I won't comment on whether this is a good idea, because I doubt I'd
ever use it either way, but what you're asking for can be done using a
descriptor.[1] Just check whether the "instance" argument is None to
know if it was called from the class.

-Gul

[1] http://docs.python.org/ref/descriptors.html

Adrian Holovaty

unread,
Jul 12, 2007, 4:40:33 PM7/12/07
to django-d...@googlegroups.com
On 7/12/07, Todd O'Bryan <toddo...@mac.com> wrote:
> Is there a good reason not to do something like the following in
> django.db.models.Model?
>
> def form(self):
> return form_for_instance(self)
>
> @classmethod
> def form(cls):
> return form_for_model(cls)

Yes -- the good reasons against it are that it ties form logic to the
model and quashes the field namespace (disallowing you from having a
field named "form").

Adrian

--
Adrian Holovaty
holovaty.com | djangoproject.com

Todd O'Bryan

unread,
Jul 12, 2007, 6:21:52 PM7/12/07
to django-d...@googlegroups.com
On Thu, 2007-07-12 at 15:40 -0500, Adrian Holovaty wrote:
> On 7/12/07, Todd O'Bryan <toddo...@mac.com> wrote:
> > Is there a good reason not to do something like the following in
> > django.db.models.Model?
> >
> > def form(self):
> > return form_for_instance(self)
> >
> > @classmethod
> > def form(cls):
> > return form_for_model(cls)
>
> Yes -- the good reasons against it are that it ties form logic to the
> model and quashes the field namespace (disallowing you from having a
> field named "form").

How separate do you intend the models and forms to be? As it stands
right now, forms can have validators, but if you actually want to
validate programmatically what you're doing to the database, you have to
repeat any validation code you put in the form somewhere in your model.

For example, I'm storing ISBNs. They have a check digit. I can write a
nice ISBN validator that checks the check digit, but I have to attach it
to the form *and* figure out somewhere to call it in the model to be
sure illegal ISBNs don't get stored to the database. If I ever get
around to trying to check forms client-side, I'll need to supply that
information in a third way. (Ignore briefly the fact that client-side
validation would have to be in JavaScript.)

The thing that's annoying is that this is really an attribute of the
model. It's a fact about the data, not a fact about the form, which is
just a way to view and modify the data.

form_for_instance and form_for_model are great, but if you have to tweak
the form in any way (say you want to use a non-default widget for one of
the data fields) and want to use it more than once, you're reduced to
defining a new Form class or at least a new callback function to control
the field types. And where do you put that? For me, it's pretty clearly
an attribute of the model in the same way that a utility method would
be, because it just doesn't have any meaning without the model. So you
put it in the model.

This may be heresy, but I have to put it out there:

Why not just admit that models are often manipulated by forms and
provide support for things like validators and form fields in the models
themselves. What would be so wrong with:

class Book(models.Model):
isbn = models.CharField(maxlength=13,
form_field=forms.CharField(
widget=forms.TextInput(attrs={'size':'13'})),
validator = isbn_validator)
...

or even better, perhaps,

class Book(models.Model):
isbn = models.CharField(maxlength=13,
widget_attrs={'size':'13'},
validator=isbn_validator)
...


Obviously it would be possible to ignore the form tie-ins completely.

You would still need form validators that aren't tied to models, and
would still have the ability to create forms that were tied to the model
but different from the ones that the model fields specified, but you'd
get to specify all the stuff about your data (including, I'll admit, a
little bit about the data's presentation, but note that that's
completely optional and you're not obligated to do it) in one place.

On the other hand, as has happened several times before, maybe I'm
making this way more complicated than it needs to be because I'm missing
some little piece of code that would make all of this much less
disjointed.

OK...feel free to berate me. :-)

Todd

Collin Grady

unread,
Jul 12, 2007, 7:38:24 PM7/12/07
to Django developers
This doesn't seem a far step from the model fields being able to
return appropriate form fields, which they already do, so there's some
tie-in already :)

Malcolm Tredinnick

unread,
Jul 13, 2007, 12:43:09 AM7/13/07
to django-d...@googlegroups.com
On Thu, 2007-07-12 at 18:21 -0400, Todd O'Bryan wrote:

> form_for_instance and form_for_model are great, but if you have to tweak
> the form in any way (say you want to use a non-default widget for one of
> the data fields) and want to use it more than once, you're reduced to
> defining a new Form class or at least a new callback function to control
> the field types. And where do you put that? For me, it's pretty clearly
> an attribute of the model in the same way that a utility method would
> be, because it just doesn't have any meaning without the model. So you
> put it in the model.

You're projecting your particular design preferences onto everything
else here. Not a horrible thing, since everybody has different
preferences. However, you are also making a mountain out of a molehill
in the process: you are talking about trying to save one import.

The other way to look at the functionality you are doing is that they
don't belong anywhere near the models, since one model is not tied to a
single form. models and forms may (or may not) intersect. That
intersection will often not happen in the model file, since the model is
the data representation. It might happen in the view file, if it's small
enough. You might decide to create a file (or module) containing a bunch
of forms for a partiular presentation format. In any case, the form
*processing* is going to happen in the view (or be controlled by the
view). It's a presentation-related piece of functionality.

To which you will answer, "but I think it should go in the model." Which
can already be done. That's the beauty of namespaces, aliases and
imports. Forcing it into the model space is a compulsory merging of two
not necessarily related pieces of functionality. The argument that we
already have the slightly dirty formfield() method in db.models.fields
is not really a counter, either. It's not a perfect construct (I would
rather have tied model fields to a corresponding form field in the forms
namespace, since it's presentation, not semantic), but it shouldn't be
used as the top end of a slippery slope to make things worse.

I'm -1 one on this for pretty much the same reasons as Adrian. It's just
not that big a deal even for people who want to work the way you do.

Regards,
Malcolm

--
Despite the cost of living, have you noticed how popular it remains?
http://www.pointy-stick.com/blog/

Todd O'Bryan

unread,
Jul 13, 2007, 1:31:52 AM7/13/07
to django-d...@googlegroups.com
On Fri, 2007-07-13 at 14:43 +1000, Malcolm Tredinnick wrote:

> I'm -1 one on this for pretty much the same reasons as Adrian. It's just
> not that big a deal even for people who want to work the way you do.

I should have re-subjected my second post, since I'm willing to accept
the loss of a possible field named form in the model as sufficient
reason for keeping form_for_instance and form_for_model outside the
model space.

My question for the rest of the second message was a question of how
much separation between forms and models there should be. For me the
validation issue is key. We currently don't have any standard way to
programmatically validate model fields, but we can validate forms before
we stick them into model fields. Seems like it'd be easy to avoid some
duplication if we just admit the possibility that model fields can
optionally specify something about their form fields.

Todd

James Bennett

unread,
Jul 13, 2007, 1:44:33 AM7/13/07
to django-d...@googlegroups.com
On 7/13/07, Todd O'Bryan <toddo...@mac.com> wrote:
> My question for the rest of the second message was a question of how
> much separation between forms and models there should be. For me the
> validation issue is key. We currently don't have any standard way to
> programmatically validate model fields, but we can validate forms before
> we stick them into model fields. Seems like it'd be easy to avoid some
> duplication if we just admit the possibility that model fields can
> optionally specify something about their form fields.

My personal opinion: the less coupling between models and forms, the
better. I'd even go so far as to suggest (though I don't think it'll
happen) that form_for_model and form_for_instance should go live in
django.shortcuts with the other cross-cutting utility functions ;)


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

Malcolm Tredinnick

unread,
Jul 13, 2007, 1:45:24 AM7/13/07
to django-d...@googlegroups.com

Okay, I understand where you're coming from. I think your concerns are
mitigated somewhat by realising you are talking about something we are
going to fix prior to 1.0: adding model-aware validation. The rough way
this works is that you populate your model's fields, then call the
validate() method (however it ends up being spelt) and it will raised
ValidationError and provide access to an error dictionary, etc. All
similar to forms, but related to the model.

Thus, if your validation requirements are mostly related to the data
semantics of a particular model, it will rightly belong in the model's
validation. Other pieces of validation will belong more naturally to
form field validation. So, I think you'll find that each piece of the
validation mechanics will end up in only one place.

This is actually a piece of work I want to get onto really soon, since
it keeps coming up and we keep saying we're going to fix it. It isn't
that hard, since Adrian did most of the hard work early last year. I
have a couple of other things to do first, but it might be something I
can get mostly done before OSCON and then smack it around with some of
the people there a bit, too (as well as the no-doubt endless mailing
list threads).

Regards,
Malcolm

--
Monday is an awful way to spend 1/7th of your life.
http://www.pointy-stick.com/blog/

Peter Nixon

unread,
Jul 13, 2007, 4:19:58 AM7/13/07
to django-d...@googlegroups.com
On Fri 13 Jul 2007, Todd O'Bryan wrote:
> On Thu, 2007-07-12 at 15:40 -0500, Adrian Holovaty wrote:
> > On 7/12/07, Todd O'Bryan <toddo...@mac.com> wrote:
> > > Is there a good reason not to do something like the following in
> > > django.db.models.Model?
> > >
> > > def form(self):
> > > return form_for_instance(self)
> > >
> > > @classmethod
> > > def form(cls):
> > > return form_for_model(cls)
> >
> > Yes -- the good reasons against it are that it ties form logic to the
> > model and quashes the field namespace (disallowing you from having a
> > field named "form").
>
> How separate do you intend the models and forms to be? As it stands
> right now, forms can have validators, but if you actually want to
> validate programmatically what you're doing to the database, you have to
> repeat any validation code you put in the form somewhere in your model.
>
> For example, I'm storing ISBNs. They have a check digit. I can write a
> nice ISBN validator that checks the check digit, but I have to attach it
> to the form *and* figure out somewhere to call it in the model to be
> sure illegal ISBNs don't get stored to the database. If I ever get
> around to trying to check forms client-side, I'll need to supply that
> information in a third way. (Ignore briefly the fact that client-side
> validation would have to be in JavaScript.)

Off topic I know, but FWIW Postgresql has a ISBN specific datatype which
might be of use in your situation.

Cheers

--

Peter Nixon
http://peternixon.net/

Reply all
Reply to author
Forward
0 new messages