What a fantastic proposal. I have some concerns, but I'm not sure if
any of them have to do with my misunderstanding.

1. The {% load %} mechanism can get ugly, fast. What if I am rendering
multiple different forms on the same page? {% load %} {% form %} {%
load %} {% form %} feels mildly unclean to me. The only alternative
that comes to (my) mind is specifying an alternate renderer in the {%
form %} tag, but that will add yet another argument to a tag that
already has an unwieldy list of (possible) arguments.

2. The {% load %} mechanism is overloaded with two orthogonal
functions: doctype selection and custom rendering. If I write a
reusable widget that need some custom rendering fanciness, then the
logic for rendering the widget goes in one place (the widget class,
based on the doctype kwarg) but I have to provide several custom
renderers, one for each doctype. Seems inconsistent to me. I like the
idea of widgets being responsible for the widget and chrome being
responsible for everything else, but feel like the two concerns might
need to be represented individually. Doctype doesn't change throughout
the document, but renderers might.

3. Related to #2, what is the behavior of a Widget if I ask it for a
doctype it doesn't support?

I need to think about the renderer/chrome bits some more, will weigh
in again in the morning with a clear head.

-I

On Jul 11, 6:36 pm, Russell Keith-Magee <freakboy3...@gmail.com>
wrote:
> Hi all,
>
> I'd like to propose a few extensions to Django's form library for 1.3.
> I'm still working on some fine details, but before I get too far, I'd
> like to field opinions so that I can:
>
>  * Discover any edge cases I've missed in my analysis
>  * Field any criticisms from people with more design/frontend
> experience than myself
>  * Determine any related problems that we have the opportunity to
> solve at the same time
>  * Find out if there is anyone in the community who is interested in
> helping out.
>
> Apologies in advance for the length, but there's a lot of detail to cover.
>
> With this proposal, I'd like to address three problems:
>
>  1. The layout problem. Django's forms can be rendered "as_ul",
> "as_table" or "as_p", and that's it. These layout schemes can be
> overridden and customized if you know what you're doing, but it's not
> easy to do so. Furthermore, visual layout concerns aren't separated
> from data processing concerns. You need to write (and install) a form
> subclass to implement your own form layout. Although it's good
> app-writing practice to ensure that forms can be easily substituted,
> it's not an enforced or universal practice.
>
>  2. The widget problem. This is a variant on the previous point. A
> designer that wants to use a specialized calendar widget for a date
> field needs to modify form code. This is a complexity that shouldn't
> exist; a designer should be able to specify the widget library that
> needs to be used (with all it's required rendering requirements,
> javascript triggers etc) without modifying views and form processing
> code.
>
>  3. The DOCTYPE problem. Most importantly, there is the closing slash
> problem, but the introduction of HTML5 also means that there are
> richer input types like <input type="email"> that aren't available in
> HTML4 or XHTML1. Django currently outputs XHTML1 unconditionally, and
> has no support for the new HTML5 input types.
>
> To solve these three problems, I'd like to propose that we add (and
> promote) the use of a new approach to form rendering, based around the
> use of a new {% form %} template tag. This proposal has some
> similarities to a proposal made by in the 1.2 feature phase [1] -- but
> that proposal was only aiming to solve the doctype issue.
>
> [1]http://groups.google.com/group/django-developers/browse_thread/thread...
>
> So: What I'm proposing is that we introduce a new template tag: {% form %}.
>
> How does this solve the three problems?
>
> Layout
> ------
>
> The simplest approach for rendering a form would become:
>
> {% form myform %}
>
> This would effectively implement the as_table rendering strategy, just
> as {{ myform }} does right now.
>
> If we want a different rendering, we exploit the fact that {% load
> %}ing a template library will override any template tags that are
> redefined. {% form %} would be defined as part of the default template
> tag library, implementing the 'as_table' strategy. However, if we load
> a library that also defines the {% form %} tag, that definition will
> override the base definition. If we want to use a custom rendering
> style, we can get that by simply loading a different renderer that
> implements that style:
>
> {% load custom_renderer %}
> {% form myform %}
>
> Django would ship with {% form %} implementations of the 'as_p' and
> 'as_ul' strategies, so getting 'as_p' rendering would mean:
>
> {% load xhtml_p_forms %}
> {% form myform %}
>
> {% form %} is just a template tag, but the default implementation
> would be designed in such a way that it could be easily subclassed to
> alter the rendering strategy for the form. I'm still tinkering with
> details here, but broadly, the intention is to expose a similar
> interface to that used by Form.as_*()  -- that is, returning format
> strings that specify like '<td>%(errors)s%(field)s%(help_text)s</td>'
> to define the rendering strategy; this would be implemented as a
> function so that forms could decide on a per field basis what output
> format is appropriate. However, unlike the existing as_*() approach,
> you don't need to have access to the form in order to use the
> different renderer, which means you can define and apply your
> rendering strategy independent of the view and form code.
>
> Since the form renderer exposes the logic for rendering individual
> form rows, we can also expose the ability to render individual form
> fields, plus the non-field errors and hidden fields:
>
> {% form myform errors %}  -- All the non-field form errors, plus
> hidden field errors
> {% form myform field birthdate %} - output a full row for the
> birthdate field (wrappers, label, errors, help etc)
> {% form myform hidden %} -- output all the hidden fields
>
> This just exposes the internal mechanics that makes the full-form
> rendering of {% form myform %} possible.
>
> Widgets
> -------
>
> The second problem is support for widgets and other rendering
> customization. This can be addressed using extra arguments to the {%
> form %} tag when rendering individual fields:
>
> {% form myform field birthdate using calendar %}
>
> This instructs the rendering of the birthday DateField to use the
> 'calendar' chrome.
>
> What is chrome? Chrome is an attempt to overcome the practical
> limitations of Django's Widgets.
>
> When we introduced newforms, the intention was that widgets would be
> the point at which form rendering would be customized. If a developer
> wanted to use a rich Javascript rendering for the calendar, they would
> define a custom Date widget, override the render() method to introduce
> the appropriate Javascript and CSS hooks, and then define a form with
> fields that specify the use of that widget. The way admin uses widgets
> is probably the best example of how this was intended to work.
>
> However, in practice, widgets aren't used like this. Widgets aren't
> trivial to define, and they aren't easy to deploy, either. The
> convoluted mechanics that ModelAdmin goes through in order to install
> a custom calendar widget is probably the best demonstration of why
> this idea hasn't taken off -- it's just too complex.
>
> It's also not simple to "just use a different widget", either. The
> interplay between form and widget is sufficiently complex that the
> only time at which you can define the widget that is to be used is
> when you define the field itself. This means that widget choice is
> closely bound to form design, which makes it nigh impossible define a
> scheme for deploying widgets as part of the template rendering layer.
>
> So -- the idea behind chrome is to separate the raw HTML input
> mechanism from the bits that make the input look good on the rendered
> page.
>
> Widgets remain as they are, but we de-emphasize their use as a
> rendering customization tool. They become little more than the
> decision over which HTML <input> (or <select>) will be used. It's
> still important that widgets are distinct from fields. After all,
> there are times when there are multiple input options for particular
> field data -- consider the case of a location field, that will require
> a custom lat/long split widget.
>
> It's the job of the chrome to make the <input> provided by the Widget
> look pretty. This might mean adding extra classes to the rendering of
> the <input>; it might mean putting placeholder <div>s or other
> elements around the input; or it might mean registering that a
> javascript block is required to support that input.
>
> Chrome can also be layered. Different pieces of chrome could perform
> different roles, and could be applied one after he other. For example
> in the following:
>
> {% form myform field birthdate using calendar important %}
>
> The "calendar" chrome might add the javascript and classes to enable
> the rich widget, where the "important" chrome might just add CSS
> classes that enables a particular field to be rendered in a particular
> style.
>
> Chrome can also be parameterized; for example:
>
> {% form myform field name using autocomplete:"name_autocomplete" %}
>
> might define a chrome that implements an Ajax autocomplete widget
> using the named URL "name_autocomplete" as a data source. This has to
> potential to start giving an answer to the "Why doesn't Django do
> AJAX" monkey; Django won't provide an AJAX solution out of the box,
> but packaging a chrome that implements AJAX should be a lot easier.
>
> Chrome could also be programatically assigned as part of the form.
> Using a formfield_callback() style setup, it should be possible to
> automatically assign certain chromes to certain field types (e.g.,
> always apply the calendar chrome to DateFields).
>
> I also hope to be able to provide a solution for the problem of
> putting all the javascript for a form at the bottom of the page. At
> present, widget rendering allows you to insert a <script> tag
> immediately before or after a form element, but best practice says it
> should be contained in a single block at the end of your page.
>
> By rendering a widget using a template tag, we get the ability to set
> variables in the context. When a form field is rendered using a
> particular piece of chrome, that chrome can register that it requires
> a specific javascript trigger; a {% form triggers %} call can then be
> used to retrieve all the triggers that have been registered, and embed
> them in a <script> call at the end of the page.
>
> Again, I'm still working on details here, but the intention is to make
> chrome easy to define -- hopefully just a class with some fairly
> simple entry points.
>
> Doctypes
> --------
>
> The third problem is doctype support. To solve this, I propose two changes.
>
>  1) We introduce a 'doctype' argument to the render() method on
> widgets. Widgets would then be expected to generate HTML output that
> is compatible with the supplied doctype. For backwards compatibility,
> internal calls to widget.render() would need to introspect for the
> existence of the 'doctype' kwarg on the specific widget, and raise
> warnings if the kwargs isn't available.
>
>  2) We introduce a number of new default widgets to support the new
> HTML5 input types. At present, EmailField uses a TextInput by default.
> Under the new scheme, we would introduce an EmailInput, and EmailField
> would use EmailInput as the default widget. When rendered using the ...
>
> read more »

-- 
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