Hello,

There was some discussion on the current limitations of the ModelForm
API in the past couple of days on IRC, I'd like to make a proposal to
address some of them.

I wrote django-floppyforms, a library that lets you render forms using
templates instead of python code. It behaves exactly like the Django
forms library, the only difference is the import path. Import
`floppyforms as forms` and when you render a form, the HTML code comes
from templates.

There is still a limitation with ModelForms: since there is no way to
globally override the model field -> form field/widget mapping since
ModelForms fields come from the model definition. I documented this
here:

http://django-floppyforms.readthedocs.org/en/latest/differences.html#modelforms

It is possible to override widgets using the Meta.widgets dict but not
in a way that would switch all the ModelForm's fields to
template-based widgets automatically. The idea is to have custom
ModelForm subclasses with specific strategy on the model field -> form
field mapping.

This is currently possible using something called `formfield_callback`
as a ModelForm class attribute: it is simply a callback that takes a
db field and returns a form field. But this callback is a) private and
b) limited: it gets lost in the inheritance chain as described in
ticket #12915:

https://code.djangoproject.com/ticket/12915

A discussion thread was started a couple of days ago for a design
decision about formfield_callback: should we consider its behaviour as
buggy or leave it as it is? In fact formfield_callback is only used by
the admin, which has custom widgets for pretty much every db field.
This customization is done using the following methods and attributes
(all in django/contrib/admin/options.py):

FORMFIELD_FOR_DBFIELD_DEFAULTS
ModelAdmin.formfield_overrides
ModelAdmin.formfield_for_dbfield(db_field, **kwargs)
ModelAdmin.formfield_for_choicefield(db_field, **kwargs)
ModelAdmin.formfield_for_foreignkey(db_field, **kwargs)
ModelAdmin.formfield_for_manytomany(db_field, **kwargs)

In most cases those methods end up calling formfield() on the DB field
object, with some arguments for customizing the field class (wrongly
called `form_class`) and its constructor arguments (widget, label,
help_text, required, etc).

The arguments to db_field.formfield() are passed via the
formfield_callback function I mentioned earlier.

My proposal is to move that field customization API from the
ModelAdmin class back to ModelForm:

* Move formfield_for_* to ModelForm and document them as public APIs
* Deprecate `formfield_callback`
* Write an AdminModelForm class that implements all the admin form
fields and widgets customization
* Modify ModelAdmin to make use of that base class
* (maybe?) deprecate ModelForm.Meta.widgets in favor of something
similar to the admin's formfield_overrides, which is more generic.

I see the following advantages to this:

* This would allow people to have "site-wide" fields/widgets
overrides, which is a feature that the admin is already proving
useful. Write a nice date picker once, register it for all your
DateFields globally.

* Maintainers of form libraries can ship a base ModelForm class that
implements custom fields/widgets while keeping API compatibility with
Django.

Backwards-compatibility shouldn't be an issue as this touches only a
couple of ModelAdmin methods. Regarding formfield_callback, despite it
being a private API I'm not sure it can be removed safely. There are
references to it on StackOverflow and on the Django bug tracker.

I'm happy to work on a patch if core devs agree to accept this. Thoughts?

Regards,
Bruno

-- 
You received this message because you are subscribed to the Google Groups 
"Django developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to django-developers+unsubscr...@googlegroups.com.
To post to this group, send email to django-developers@googlegroups.com.
Visit this group at http://groups.google.com/group/django-developers?hl=en.
For more options, visit https://groups.google.com/groups/opt_out.


Reply via email to