Hi Russ,

First of all, thanks very much for this proposal! Form rendering has
been a major pain point for us (thus the existence of
django-form-utils), and improving it is tops on my 1.3 wishlist. I
will also be at DjangoCon and eager to sprint in this area.

Django has a really good template system. With template inheritance,
includes, and overrides, it allows the designer I work with a
remarkable amount of flexibility in producing exactly the HTML he
wants, without repeating himself. He's not a programmer (and thus
doesn't follow django-developers), but he works full-time with the
Django template system and loves it. His (frequent) complaints to me
about Django form rendering are always the same: "Why is this markup
generated deep inside Python code, and why can't I override a template
to fix it? I thought we'd moved on from PHP!"

I think it's possible to solve the problems you're aiming at here
without so much new Python machinery, just by delegating more to
templates (as Eric and Danny have already mentioned briefly). HTML is
the native language of web designers, and I'm firmly convinced that
the less we hide their HTML behind Django-specific abstractions, the
more usable the system will be.

I'll first outline in brief what I think a fully template-oriented
approach might look like, address some possible concerns with it, and
follow that with some specific comments on your proposal.

A fully template-based approach to form rendering
=================================================

You have a form object in your template. You render it using a simple
"render_form" filter:

{{ form|render_form }}

"render_form" just renders a default form rendering template (at some
sensible template path, the bikeshed could be painted
"django/forms/default.html" or some such), with the form object itself
passed into the context.

This template just does the usual iterating over form fields, and the
default one provided by Django could mimic form.as_table(). That
default template could of course be overridden for any given project,
and the "render_form" filter also accepts as argument an alternative
template to use for a particular form:

{{ form|render_form:"path/to/my/form_template.html" }}

So far, this approach is workable in current Django. This is what
django-form-utils does, and what we do in all our projects.

The next step is to have the default widgets render themselves using
templates found at a standard path
(e.g. "django/forms/widgets/textinput.html"). This template would be
passed the boundfield and widget, and so would have access to all the
information it needs to render itself correctly.

There would also be a "render_field" template filter, that would
optionally accept an arbitrary widget template path:

{{ form.fieldname|render_field:"path/to/my/widget_template.html" }}

Normally this would only be used inside a form rendering template, but
if you wanted to skip the "render_form" filter and render your form
directly using this filter repeatedly (or inside a loop), that's
possible too.

All of the problems you identified as targets are now trivially
solvable, just by giving the designer direct access to the template
system, without all kinds of magical abstractions in between. Form
layouts can be fully controlled in templates. Widget markup can be
fully controlled in templates (and of course can validate to whatever
doctype you want it to, without any special machinery for that).

The last target you identified, allowing widget-related JS to be
rendered in a block at the bottom of the template, can be solved
simply with another filter and set of templates:

{{ form|render_form_js }}

Which would look for templates such as
"django/forms/widgets/js/textinput.html" for each widget in the form,
rendering nothing for a widget if that template doesn't exist. This
filter could also optionally accept a path to a template containing
form-wide JS. These templates would probably just contain script tags,
either linking to an external JS file or containing inline JS. (IMO,
collating these into a single script tag is out of scope and better
handled by a dedicated solution like django-compressor; though the
possibility of referencing a single form-wide JS template opens the
door to other possible solutions).

Advantages
----------

- As mentioned, HTML is the native language for a web designer: this
  exposes it directly and simply.

- Similarity to existing approach: this is a less radical
  change. Rather than introducing a new template tag that does all
  kinds of magic behind the scenes, and several new abstractions
  (renderers, chrome) to boot, this just leverages existing, proven,
  well-understood tools, and the resulting templates look more similar
  to existing form-rendering templates.

- No need for special-casing things like doctypes, adding attributes
  directly to a widget-generated HTML input tag. Everything is in
  templates, everything is equally customizable via that familiar
  route.

Possible objections
-------------------

- Russ mentions the "complexity" of "getting all the template paths
  lined up." I'm not sure what the issue is here; Django template
  overrides use a simple path-based scheme, and it's a proven system
  that works. Designers use it successfully all the time. I think
  introducing new complexity in the form of new Python abstractions is
  more likely to be a real problem.

- Concerns about filter syntax. The first two concerns Russ raised are
  only issues if many filters have to be strung together, which this
  proposal does not require. The third objection is predicated on the
  necessity of doing tricks with the context to load widget JS; again,
  a template-based solution doesn't need this.

- "Too much work to override all these widget templates just to switch
  doctypes!" Again, I think the complexity of dedicated Python
  machinery is likely to be worse. Django's philosophy (and James
  Bennett's philosophy of reusable app templates, referenced above)
  assume that templates should be per-project: why should forms or
  widgets be different? Packaging up a reusable app with a set of
  generic "HTML5" or "XHTML1" widget templates is almost certainly
  simpler (and less bug prone) than writing the equivalent Python code
  for the customized {% form %} tag renderer.

- Possible performance issues with rendering too many templates. I
  think for many people this will not in fact be a problem, and for
  those for whom it is, we now have the cached template loader, which
  should make it disappear.

Specific comments on Russ' proposal
===================================

- Using {% load %} to import new renderers is insufficiently flexible
  (as has already been noted). Templatetag namespacing might help with
  this, and is a good idea in its own right; but IMO this is still
  more complex and less flexible than the template-based approach.

- I'm skeptical that the "chrome" abstraction buys very much in
  exchange for the overhead of the new concept and the syntactical and
  registration problems. As mentioned above, in practice I don't think
  layering different chromes on top of each other will work very
  often; they'll frequently have to be manually integrated into a new
  unified chrome anyway.

  I'm also concerned that it's still insufficiently flexible; the core
  input tag markup will still be hardcoded in the widget, thus
  requiring special-cases for anything in it that you might want to
  tweak (doctype, extra classes or attributes). This is the big one
  for me, as it means our core problem with the current system (markup
  hardcoded in Python code) would not be fully fixed.

  In contrast, being able to directly edit widget templates for your
  specific cases is simpler, more intuitive for designers, and fully
  flexible.

- Big +1 to adding new widgets for new HTML5 input types! This is a
  necessity for good HTML5 support regardless of which approach is
  used for rendering.

Again, thanks for kickstarting the discussion with a
well-thought-through proposal! In the end if the renderer/chrome
proposal is implemented, I think we'd probably still be able to
implement much of the template-based system as a custom renderer (as
you mentioned), except for the problem with core widget rendering. I'm
just not sure what practical benefits would be gained over just
implementing the template-based system directly.

cheers,

Carl

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

Reply via email to