Form Rendering API Proposal

257 views
Skip to first unread message

Idan Gazit

unread,
Jun 23, 2011, 7:25:34 AM6/23/11
to django-d...@googlegroups.com
At DjangoCon Europe 2011, Gregor Müllegger gave a great lightning talk about his work on a revised form rendering API:


I sat down with Gregor, Jannis, Russell, Alex, Ericflo, Andrew Godwin, and many other fine minds at the sprints to think about his proposal and help refine it. At the risk of starting a bikeshedding war, I'd like to share the results of the sprints and get some feedback on our proposed approach.

I'm very pleased with the resulting API. It has one significant wart, but having chewed on the alternatives, I think there is consensus that this API represents a good compromise between purity and practical considerations.


# Goals

The existing rendering facilities for forms aren't bad, but they suffer from the following drawbacks:

1. All-or-nothing: if you'd like to customize one small part of form rendering, you have to go the whole hog and write out a ton of boilerplate.
2. "Frontend developers need not apply": large parts of form rendering happen in Python. If a frontend dev wants to customize certain aspects of form.as_p's output, they've got to bust out the python.
3. Prevents modularity and code re-use: with large bits of markup locked inside .py files, it's difficult for designers to share reusable form patterns.
4. DOCTYPE lock-in: designers are forced to use the XML style of tags, breaking validation of other doctypes when forms are present.

The new form rendering API addresses these drawbacks, and as such, has the following desirable properties:

1. No backwards-incompatible changes: the new API coexists peacefully with the old one, allowing for a smooth transition and deprecation of the old formrendering API.
2. Simple beginner usage: if all you're comfortable using is {{ form.as_p }}, the new API retains a similarly brief approach.
3. Vastly better customizability: the new API provides an expressive and flexible means for specifying how forms should be rendered, and allows you to override just the parts that need changing.
4. Significant improvements to the rendering of form rows with multiple fields (for example, first and last name, or credit card and expiration).
5. Dogfooding: the new API does have some new tags, but a lot of the internals and examples for customization are just using the existing template system. This provides a lot of flexibility, but it also means that the new API should feel familiar to existing developers.
6. Modularity: it will be possible to produce reusable libraries of form rendering templates.


# The anatomy of form rendering templates

Forms are composed of three logical units:

1. A template which represents the form itself, including some form-specific configuration.
2. A template which represents a given "row" in a form. A row is an abstract concept, and doesn't have to represent a horizontal slice of anything (but usually does). The existing form rendering API produces rows in the shape of <p>'s, <li>'s, and <tr>'s. In the new form rendering API, Django supplies the formrow templates for all three of these row "types", but users can easily add new ones of these to create a formrow based on <div>'s, for example.
3. A template which represents a given widget in the form. These are the widgets which are currently provided by the framework,  but supplied as templates instead of being buried in widgets.py.

# Examples.
I've prepared example templates for each of these; check out https://github.com/idangazit/formrendering after reading the following tutorial. These represent a good starting point for developing the form templates distributed with Django.

# API Usage Tutorial

The new API introduces the following template tags:

- form
- formconfig
- formrow
- formfield

For the following examples, "myform" is a form instance present in the template's context.

# {% form %}

The new form rendering API introduces a {% form %} tag:
 
  {% form myform %} {# equivalent to the following line #}
  {% form myform using "forms/layouts/table.html" %}
  {% form myform using "forms/layouts/p.html" %}
  {% form myform using "forms/layouts/ul.html" %}

In its simplest incarnation, it works like the existing form.as_XX methods. Users select a row type by means of the optional using argument. Django will need to supply the existing row types (table, p, ul) to ease the transition.

Users can specify their own form templates using the same syntax:

  {% form myform using "fancy_div.html" %}
  {% form myotherform using "fancy_div.html" %}

Like the include tag, form can pass context into its child template:

  {% form myform using "myform.html" with greeting="Hi!" %}
  {% form myform using "myform.html" with greeting="Hi!" only %}

The form tag can also load a configuration inline, as follows:

  {% form myform using %}
    ... layout goes here ...
  {% endform %}

The template contents that go inside a form block are identical to those that can be included from an external template file, as in the previous examples.

The form tag has one more trick up its sleeve: it can take multiple forms as an argument:

  {% form myform1 myform2 using "forms/layouts/p.html" %}

# Form Templates

In the Django-supplied templates, these would be rendered sequentially, with form errors at the top of every form. However, users can write their own form templates which might mix fields from each form, and group both forms' errors at the top.

The standard form layouts have a lot of commonality, and so they rely on template inheritance for DRYness:


Form layout templates are in the business of spitting out all of the fields in a form, one row at a time. This is usually accomplished with the {% formrow %} tag.

## {% formrow %}

The formrow tag is responsible for producing a form's row, and is only valid within the scope of a form tag. The formrow tag takes an optional template. If a template is not specified, the default is used (table). Changing the template to produce <p>'s is accomplished thusly:

  {% for field in form %}
    {% formrow field using "forms/rows/p.html%}
  {% endfor %}

Formrow, like form, can take multiple fields:

  {% formrow firstname lastname using "myfancynamerow.html" %}

Django supplies the formrow templates for table, ul, and p. Like the form layout templates, they utilize inheritance to keep things DRY:


Each form row is responsible for rendering the fields passed to it.

## {% formfield %}

The formfield tag provides the ability to customize widget rendering for one or more fields. Usually, they are used in the context of a formrow template:


The field templates are a replacement for the HTML currently buried in the render() methods of forms/widgets.py. Instead of building the HTML strings in python, widgets.py should avail itself of the templates to produce its output.

The default usage of the tag will automatically select a relevant widget using the existing heuristics. Assuming a form with a CharField named "first_name":

  {% formfield first_name %}

Will produce an <input> with the relevant properties.

If customization is desired, users can write their own widget templates:

  {% formfield publish_date using "mywidgets/fancydatetimepicker.html" %}

Bruno Renié's django-floppyforms (https://github.com/brutasse/django-floppyforms) is a lovely example of the kinds of things accomplishable with template-based form widgets.

# Controlling default form rendering behavior

Often, overriding a specific bit of form rendering behavior is desirable. For example:

- You want to use a different widget for one specific field in your form.
- You want to use a different widget for all fields of a specific type in your form.
- You want to specify a different default row style for some or all fields in a form.

To that end, there is the {% formconfig %} tag. It is only valid inside a {% form %} block (or when pulled in via a form layout template). It affects form state, and as such represents the one part of this proposal that I'm not entirely happy with. That being said, I like all of its other properties, and couldn't think of something better which didn't simply shunt the complexity to another location.

The formconfig tag is multimodal: the first argument indicates which behavior to configure. If desired, new modes can be added in the future, and template designers need not hunt around the docs for new configuration arguments; they'd be grouped together under the {% form %} reference documentation.

As shipped with Django, formconfig can operate on widgets and rows:

{% formconfig widget widgets.Textarea for "comment" %}
{% formconfig row using "forms/rows/ul.html" %}

The first statement instructs the form to use a textarea widget for any formfield named "comment." The second instructs the form to use ul's as the default formrow template anytime a {% formrow field %} is encountered.

These statements affect global state within a form, and can be reconfigured multiple times within a form layout:

  {% formconfig row using "forms/rows/p.html" %}
  {% formrow field1 %}
  {% formrow field2 %}
  {% formconfig row using "myrows/fancydiv.html" %}
  {% formrow field3 %}

Similarly, default widgets can be set and reset:

  {% formconfig widget widgets.TextArea for forms.CharField %}
  {% formrow bio %}
  {% formrow interests %}
  {% formconfig widget widgets.TextInput for forms.CharField %}
  {% formrow hometown %}
  {% formrow zodiac_sign %}


# Conclusion

Hopefully, this proposal will stimulate discussion about how best to architect the form rendering API. I'd love to see more opinion from more frontend developers; if you have access to such people, please get them to check this out and weigh in.

Don't forget to take a look at https://github.com/idangazit/formrendering, the templates are a helpful illustration of the possibilities.

# Also, I want a pony

This might be a good opportunity to change something I've long considered very un-Django-like in Django. Form rendering defaults to using tables for layout, which is semantically icky. How about we make the new default be p, or better yet, div? To that end, I've supplied div form layout & row templates.


Harro

unread,
Jun 23, 2011, 8:11:37 AM6/23/11
to django-d...@googlegroups.com
Two things:

- Will the as_* methods on forms be deprecated? They seem to be a nice shorter version then the new way to do it.

- I assume the formconfig calls are for the current context, but can I set them in the base.html and then automatically have them used in all templates extending the base.html and templates included in a template?

Gregor Müllegger

unread,
Jun 23, 2011, 8:30:24 AM6/23/11
to django-d...@googlegroups.com
Hi Harro,

2011/6/23 Harro <hvdk...@gmail.com>:


> Two things:
> - Will the as_* methods on forms be deprecated? They seem to be a nice
> shorter version then the new way to do it.

The plan is to deprecate them. First reason is that the new approach
is more explicit of what happens. The second and main reason is, that
as_* are methods on the form. IMO the form shouldn't know much about
its representation or how it's rendered. I see it like a model that
doesn't know about its view (in the MVC sense, not model in django
sense).

> - I assume the formconfig calls are for the current context, but can I set
> them in the base.html and then automatically have them used in all templates
> extending the base.html and templates included in a template?

They are limitted in the scope of the tag where they are used. So
formconfig tags cannot break out of a form tag, or the template that
is included by the form tag. You are not able to set global defaults
as of configuring all forms in your base.html, since using them
outside of a {% form %} will raise a TemplateSyntaxError. However you
can achieve something similiar for having a generic base template for
all your form layouts.

--
Servus,
Gregor Müllegger

Jonas H.

unread,
Jun 23, 2011, 8:33:16 AM6/23/11
to django-d...@googlegroups.com
On 06/23/2011 02:11 PM, Harro wrote:
> - Will the as_* methods on forms be deprecated? They seem to be a nice
> shorter version then the new way to do it.

I'd rather provide a shorter version of {% form %} for built-in layouts:

{% form foobar 'table' %}
as shorthand for
{% form foobar 'forms/layouts/table.html' %}

This behaviour could even be extended to automagic template selection,
so if you do {% form ... 'foo' %} Django searches for a 'foo.html'
template in "project/current_app/templates/current_app/forms/layouts/",
then "project/templates/forms/layouts/", falling back to
"django/forms/templates/forms/layouts/" if none of those directories
contains the desired file. Your proposal doesn't tell anything about
where form templates are searched, is something similar to this intended?

Daniel Moisset

unread,
Jun 23, 2011, 9:06:05 AM6/23/11
to django-d...@googlegroups.com
On Thu, Jun 23, 2011 at 8:25 AM, Idan Gazit <id...@gazit.me> wrote:
> At DjangoCon Europe 2011, Gregor Müllegger gave a great lightning talk about
> his work on a revised form rendering API:
> http://www.scribd.com/doc/57270484/Djangocon-EU-2011-Revised-Form-Rendering-Lightning-Talk-by-Gregor-Mullegger
> I sat down with Gregor, Jannis, Russell, Alex, Ericflo, Andrew Godwin, and
> many other fine minds at the sprints to think about his proposal and help
> refine it. At the risk of starting a bikeshedding war, I'd like to share the
> results of the sprints and get some feedback on our proposed approach.
> I'm very pleased with the resulting API. It has one significant wart, but
> having chewed on the alternatives, I think there is consensus that this API
> represents a good compromise between purity and practical considerations.
>

It seems really nice (said as someone who has done almost as much
frontend as backend work)

What is the "significant wart" ?

Daniel

Idan Gazit

unread,
Jun 23, 2011, 9:11:06 AM6/23/11
to django-d...@googlegroups.com


On Thursday, June 23, 2011 4:06:05 PM UTC+3, dmoisset wrote:

What is the "significant wart" ?


The formconfig tag is a little bit "magical"; there's no other example in the template langauge of something explicitly affecting state in the same fashion. Even things like the "with" tag are self-bounding.

Granted, formconfig is scoped to the form block, but stil. Feels a little bit dirty to me.

-I

Daniel Moisset

unread,
Jun 23, 2011, 9:39:10 AM6/23/11
to django-d...@googlegroups.com

Maybe bikeshedding, but have you considered storing the configuration
on automatically created variables, and then setting those with a {%
with %} tag?

Automatically created variables is not a new concept ({% for %}
creates a "forloop" variable). Let me translate your previous example
to this idea to see what you think. I chose names rather arbitrarily,
probably they can be better thought out:

{% formconfig widget widgets.Textarea for "comment" %}

translates to...

{% with formconfig_widget_comment=widgets.Textarea %}
... the rest of the form here
{% endwith %}

And,

{% formconfig widget widgets.Textarea for "comment" %}

translates to...

{% with formconfig_row="forms/rows/ul.html" %}
... the rest of the form here
{% endwith %}

And,

{% formconfig row using "forms/rows/p.html" %}
{% formrow field1 %}
{% formrow field2 %}
{% formconfig row using "myrows/fancydiv.html" %}
{% formrow field3 %}

...translates to...

{% with formconfig_row="forms/rows/p.html" %}


{% formrow field1 %}
{% formrow field2 %}

{% endwith %}
{% with formconfig_row="myrows/fancydiv.html" %}
{% formrow field3 %}
{% endwith %}

Additionally, in the example above, it is easy to set a row renderer
for a single row and then go back to the default without having to
specifying explicitly:

{% formrow field1 %}
{% formrow field2 %}

{% with formconfig_row="myrows/fancydiv.html" %}
{% formrow field3 %}
{% endwith %}
{% formrow field4 %}

Actually, in my ideal world, instead of names with underscores, I'd
like to have formconfig.row and formconfig.widgets.fieldname, but that
would require adding some extra support for dotted vars to {% with %}
that might be dangerous

As an additional "I want a pony" feature, maybe if you are allowed to
define formconfig vars *outside* the form, and getting the form to
inherit it, you can make settings that span more than one form (and
still being explicitly scoped by a {% with %} tag)

What do you think?

Regards,
D.

Preston Timmons

unread,
Jun 23, 2011, 1:56:31 PM6/23/11
to Django developers
This looks excellent so far.

Do {% formfield %} and {% formrow %} accept context like {% form %}
does?

Is there a way with {% formfield %} or {% formrow %} to set custom
attributes like placeholder, autocorrect, etc.? I find this common
requirement when optimizing forms for mobile devices.

Thanks,

Preston

Chris Pickett

unread,
Jun 23, 2011, 9:25:06 AM6/23/11
to django-d...@googlegroups.com
I've been working on something almost identical to this. But your formconfig is what really gives it that last piece that I didn't have, very nice! 

I doubt that it's helpful, but I just put my code up at: https://github.com/bunchesofdonald/django_amaro it's still very early stages, and needs cleaned up, etc. I was just trying for a proof of concept.

Let me know if you need another developer and I'll be there, this is one of my biggest pain points with Django.

Chris

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

Jacob Kaplan-Moss

unread,
Jun 23, 2011, 5:43:25 PM6/23/11
to django-d...@googlegroups.com
Hi Idan et al. --

Thanks for putting this all together!

In general, I like this a lot, and I'm always going to defer to the
eyes of someone like Idan who spends more time wrangling templates
than I do. So I like the general gist, and I most don't mind the {%
formconfig %} business.

However, I do have a few concerns:

1. Performance: it looks, to me, like rending a basic form is going to
cause dozens of template includes and dozens of sub-renders (the form
loads a form template which loads row templates which load widget
templates). That's dozens of disk hits, and a lot of overhead for form
rendering. I worry about this overhead a lot. Django's performance has
slipped lately, and I'm really afraid this'll make things a lot worse.

So I'm going to need to see some benchmarks -- particularly in how a
simple {% form myform %} compares to {{ form.as_* }}.

The wrong performance benchmarks could result in a veto from me; this
is important.

2. Verbosity: There's a lot of tags (well, 4, but that's a lot to me)
wall-of-code stuff like
https://github.com/idangazit/formrendering/blob/master/djangocon_sketch.html#L62-83
doesn't particularly give me the warm fuzzies. I think part of the
problem is that all the tags are `form*` which makes for a bit of
"bork bork bork" there.

I think it might be possible to simplify things somewhat here, so
here's my rough thoughts:

* Keep {% form %} -- it's obvious.
* Rename {% formfield %} to {% field %} -- it won't conflict, and it's
(fairly) obvious we're talking about a *form* field since we'll
usually be saying {% field myform.whatever %}.
* Drop {% formrow %} entirely. Instead, have {% field %} generate the
whole thing you're calling a "row".
* Add {% widget %} which rendered just the field (i.e. what {%
formfield %} does now).
* Keep {% formconfig %}.

This is verging dangerously close to bikeshedding, so the syntax
either way won't change my vote much.

Thanks!

Jacob

Benoît Bryon

unread,
Jun 23, 2011, 6:05:37 PM6/23/11
to django-d...@googlegroups.com
Le 23/06/2011 14:33, Jonas H. a �crit :

> On 06/23/2011 02:11 PM, Harro wrote:
>> - Will the as_* methods on forms be deprecated? They seem to be a nice
>> shorter version then the new way to do it.
>
> I'd rather provide a shorter version of {% form %} for built-in layouts:
>
> {% form foobar 'table' %}
> as shorthand for
> {% form foobar 'forms/layouts/table.html' %}

Hi,

Legacy {{ form }} can be replaced by a short {% form %} if :

* the form tag looks for a context variable named "form" by default. This name is used by some generic views. Isn't it a kind of convention?
* the default layout is the "as_table" equivalent.

--
Benoit

Carl Meyer

unread,
Jun 23, 2011, 7:02:24 PM6/23/11
to django-d...@googlegroups.com
On 06/23/2011 04:43 PM, Jacob Kaplan-Moss wrote:
> 1. Performance: it looks, to me, like rending a basic form is going to
> cause dozens of template includes and dozens of sub-renders (the form
> loads a form template which loads row templates which load widget
> templates). That's dozens of disk hits, and a lot of overhead for form
> rendering. I worry about this overhead a lot. Django's performance has
> slipped lately, and I'm really afraid this'll make things a lot worse.
>
> So I'm going to need to see some benchmarks -- particularly in how a
> simple {% form myform %} compares to {{ form.as_* }}.
>
> The wrong performance benchmarks could result in a veto from me; this
> is important.

We've had a fair bit of discussion on this, and some benchmarks, around
Bruno's work porting django-floppyforms (templated widgets) to a core
patch (https://code.djangoproject.com/ticket/15667). The disk hits
aren't a big concern to me - if you are at a point where you have to
care about template speed, you already need to be using the cached
template loader regardless. But just plain rendering speed is a problem
- even with just widgets in templates, in some pathological cases (e.g.
a ChoiceField with tons of choices) it can be quite significantly
slower. Enough so that I put #15667 in the icebox for now, pending
seeing the impact of Armin's GSoC.

I really think templated form-rendering is a massive improvement in
Django for front-end devs, so I'm very hopeful that Armin's work can
make rendering speed a non-issue. No pressure, Armin ;-)

Carl

Jjdelc

unread,
Jun 24, 2011, 12:42:43 AM6/24/11
to Django developers
>
> {% formconfig widget widgets.Textarea for "comment" %}
> {% formconfig row using "forms/rows/ul.html" %}
>
> The first statement instructs the form to use a textarea widget for any
> formfield named "comment." The second instructs the form to use ul's as the
> default formrow template anytime a {% formrow field %} is encountered.
>

I agree in {% formconfig %} being magical. Having "widget" "row" modes
feels a bit limited and it seems unnatural to have a template change
it's syntax based on a keyword. It feels more natural to have two
separate templatetags for each behavior, but we don't want to
introduce more complexity.

It is still not clear to me about the "widget" mode, is the second
parameter a import path? I think it would be clearer to include a
template path just like "using" instead of a import path that the
designer is not aware of.

{% formconfig widget "forms/widgets/textarea.html" for "comment" %}

or even

{% formconfig widget "comment" using "forms/widgets/textarea.html" %}

in order to mantain the same syntax for {% formconfig %}.

Is it possible for a django user to overload {% formconfig %} to do
something else? or do we have to wait for a next Django release to
have new behavior?

What about an "errors" mode? or a "help text" mode. I can see why it
would be handy to be able to extend it according to developers needs.

Harro

unread,
Jun 24, 2011, 2:38:25 AM6/24/11
to django-d...@googlegroups.com
1.Disk hits can be avoided using the django.template.loaders.cached.Loader.
We have a form rendering system that uses a lot of templates and it's being used in some pretty big websites, so far I haven't notices performance issues because of form rendering and we haven't use the cached loader as of yet.
Also waiting for them to build it so it can be performance tested and then shooting it down seems a bit harsh ;-)

2. I dunno if renaming row to field is a good idea, a row can contain multiple fields.

Benoît Bryon

unread,
Jun 24, 2011, 3:03:38 AM6/24/11
to django-d...@googlegroups.com
Le 24/06/2011 06:42, Jjdelc a �crit :

> {% formconfig widget "forms/widgets/textarea.html" for "comment" %}
> or even
>
> {% formconfig widget "comment" using "forms/widgets/textarea.html" %}
>
> in order to mantain the same syntax for {% formconfig %}.

+1
A consistent syntax for all form components would be great.


Benoit

Benoît Bryon

unread,
Jun 24, 2011, 3:23:35 AM6/24/11
to django-d...@googlegroups.com
Le 24/06/2011 01:02, Carl Meyer a �crit :

> On 06/23/2011 04:43 PM, Jacob Kaplan-Moss wrote:
>> The wrong performance benchmarks could result in a veto from me; this
>> is important.
> I really think templated form-rendering is a massive improvement in
> Django for front-end devs, so I'm very hopeful that Armin's work can
> make rendering speed a non-issue. No pressure, Armin ;-)
>
> Carl
>

IMHO, templated form rendering is not an option, it already is a reality (and performance already is an issue).
Templated form rendering is already a practice of several front-end devs:

* I believe many of us tried to include template snippets to be able to reuse some form layouts (starting with {% include %})
* Since there is no de-facto convention for implementing the functionality right now, everyone uses a personal recipe.
* There is no documentation about doing it right, with performances in mind.
* Maybe hundreds of front-end devs are doing it wrong because there is no standard, with optimizations included.

I am currently using my own (shared) recipe for templated form rendering (django-formrenderingtools) and I will keep using it until there is a standard tool implementing such functionality.
For most websites I develop, I'd rather be able to develop them quickly with nice reusable code than provide extreme performances.
For the websites which require high performances, I use the cache, and if it is not enough, I can fallback to hard-coded form rendering.
So, in my case, hard-coded template form rendering is the option, whereas templated form rendering is the standard.


Le 24/06/2011 06:42, Jjdelc a �crit :

> Is it possible for a django user to overload {% formconfig %} to do


> something else? or do we have to wait for a next Django release to
> have new behavior?

I agree with Jjdelc: we should not be restricted to one hard-coded implementation.
If someone has a solution to improve performance, or to match a project specific needs, it should be possible to override the default.


Le 14/06/2011 11:38, Beno�t Bryon a �crit :

> Another idea: what if we got "layout loaders"?
> FORM_LAYOUT_LOADERS = (
> 'my.custom.app.SelectLayoutMatchingUserPreferences',
> 'another.custom.app.SelectLayoutMatchingUrl',
> 'another.custom.app.SelectTemplateMatchingFieldHtmlName',
> 'django.forms.layouts.loaders.FallbackLayoutLoader',
> )
>
Configurable loaders are one way to be flexible.
Implementing the form rendering with class-based components is another.
We already have Python classes for widgets. There could be a widget class which does not use templates and provide extreme performance.
Why not Python classes for other form elements? It would make it possible to implement both hard-coded and templated solutions.
It is a great practice in django-floppyforms: use Python components with a render() method. So let's apply this pattern to all form elements!

Could the "layout" parameter be, in fact, a reference to a Python class?

Something like this (to be consistent with the {% widget %} syntax)::

{% form AsTableLayout for form %}
{% form AsTableLayout %}<!-- equivalent to the line above if "form" is the convention for variable name -->
{% form %}<!-- equivalent to the line above if AsTableLayout is the default -->

It should be possible to provide "generic" base classes which cover most of the use cases.
And it would be possible to make hard-coded-high-performance layouts and templated ones living together.


Benoit

Benoît Bryon

unread,
Jun 24, 2011, 3:33:13 AM6/24/11
to django-d...@googlegroups.com

Le 23/06/2011 13:25, Idan Gazit a �crit :
> http://www.scribd.com/doc/57270484/Djangocon-EU-2011-Revised-Form-Rendering-Lightning-Talk-by-Gregor-Mullegger
>

I'm still not convinced by the {% form myform hidden "honeypot" %} syntax.
Isn't it a duplicate of {% widget HiddenInput for form.honeypot %} and its {% formconfig %} equivalent?

With a similar syntax we would write shortcuts like {% form not hidden "honeypot" %} or {% form textarea "honeypot" %}... I guess those are bad ideas.

Benoit


Gregor Müllegger

unread,
Jun 24, 2011, 5:45:49 AM6/24/11
to django-d...@googlegroups.com
Hi Benoît,

2011/6/24 Benoît Bryon <ben...@marmelune.net>:


>
> Le 23/06/2011 13:25, Idan Gazit a écrit :
>>
>>
>> http://www.scribd.com/doc/57270484/Djangocon-EU-2011-Revised-Form-Rendering-Lightning-Talk-by-Gregor-Mullegger
>>
>
> I'm still not convinced by the {% form myform hidden "honeypot" %} syntax.

I have also not convinced Idan for this :-) (including many other aspects of
my earlier proposal). So we sat down, creating the new proposal you've read in
the first message in this mailing thread. The "old" proposal, as in my
lightning talk, is outdated now. We want to discuss the new one here.

> Isn't it a duplicate of {% widget HiddenInput for form.honeypot %} and its
> {% formconfig %} equivalent?

Yep :)

> With a similar syntax we would write shortcuts like {% form not hidden
> "honeypot" %} or {% form textarea "honeypot" %}... I guess those are bad
> ideas.
>
> Benoit

We should have mentioned that in a clear way. Sorry for the confusion.

--
Servus,
Gregor Müllegger

Gregor Müllegger

unread,
Jun 24, 2011, 5:54:25 AM6/24/11
to django-d...@googlegroups.com
Hi Jonas,

2011/6/23 Jonas H. <jo...@lophus.org>:

Basically we wanted to avoid automagical behavior. The plan is to load the
templates like any another template, just with the installed template loaders.
We will add a form-template-loader that you can add to your TEMPLATE_LOADERS
setting that is simply a directory based template loader looking for
templates in
django/forms/templates -- making the default "shipped with django" form
layouts available.

--
Servus,
Gregor Müllegger

Gregor Müllegger

unread,
Jun 24, 2011, 6:01:19 AM6/24/11
to django-d...@googlegroups.com
Hi Preston,

2011/6/23 Preston Timmons <preston...@gmail.com>:

Yes that will be possible. We plan to have this somehow available in the
widget configuration e.g. with the current proposal:
{% formconfig widget for myform.myfield attr placeholder="Type here ..." %}

--
Servus,
Gregor Müllegger

Gregor Müllegger

unread,
Jun 24, 2011, 6:38:08 AM6/24/11
to django-d...@googlegroups.com
Hi Jacob,

2011/6/23 Jacob Kaplan-Moss <ja...@jacobian.org>:


> Hi Idan et al. --
>
> Thanks for putting this all together!
>
> In general, I like this a lot, and I'm always going to defer to the
> eyes of someone like Idan who spends more time wrangling templates
> than I do. So I like the general gist, and I most don't mind the {%
> formconfig %} business.
>
> However, I do have a few concerns:
>
> 1. Performance: it looks, to me, like rending a basic form is going to
> cause dozens of template includes and dozens of sub-renders (the form
> loads a form template which loads row templates which load widget
> templates). That's dozens of disk hits, and a lot of overhead for form
> rendering. I worry about this overhead a lot. Django's performance has
> slipped lately, and I'm really afraid this'll make things a lot worse.
>
> So I'm going to need to see some benchmarks -- particularly in how a
> simple {% form myform %} compares to {{ form.as_* }}.
>
> The wrong performance benchmarks could result in a veto from me; this
> is important.

Unfortunatelly we don't have any implementation yet, except of what Bruno did
with floppyforms, what we can benchmark. But I think we could minimize disk
hits or template-loader work by caching the rendered templates ourself. For a
form with many fields it's very likely that most of them use the same row
level template, so we can reuse this a couple of times during the rendering. I
will do my best to make things as fast as possible. Benchmark was not yet part
of my GSoC timeline but I agree that this should have a high priority.

However based on the feedback I got on djangocon and on the mailinglist yet,
the anticipation for this feature is really big. And we won't make code slower
that already exists. You only get a bit slower form rendering if you are going
to use the new mechanics, but you trade that for a much more flexible and
faster template designing. And as Benoît described in the current thread: Most
designers already use something like the proposal suggests but with a custom
{% include %} hierachy. Introducing a "special" syntax can only open up the
possibilities for performance tweaking.

>
> 2. Verbosity: There's a lot of tags (well, 4, but that's a lot to me)
> wall-of-code stuff like
> https://github.com/idangazit/formrendering/blob/master/djangocon_sketch.html#L62-83
> doesn't particularly give me the warm fuzzies. I think  part of the
> problem is that all the tags are `form*` which makes for a bit of
> "bork bork bork" there.
>
> I think it might be possible to simplify things somewhat here, so
> here's my rough thoughts:
>
> * Keep {% form %} -- it's obvious.
> * Rename {% formfield %} to {% field %} -- it won't conflict, and it's
> (fairly) obvious we're talking about a *form* field since we'll
> usually be saying {% field myform.whatever %}.
> * Drop {% formrow %} entirely. Instead, have {% field %} generate the
> whole thing you're calling a "row".

This was Idan's main point in iterating over my earlier proposal. He saw it as
a very basic usecase that fields aren't necessary one per row. He had some
excellent examples of fields, like firstname surname, that should fit into one
row. I think we should be able to make these rowdesigns reusable.

I for myself had a few situations in which I really wanted to have the feature
while implementing designs a client gave me.

> * Add {% widget %} which rendered just the field (i.e. what {%
> formfield %} does now).
> * Keep {% formconfig %}.
>
> This is verging dangerously close to bikeshedding, so the syntax
> either way won't change my vote much.
>
> Thanks!
>
> Jacob

Thanks for your input Jacob!

--
Servus,
Gregor Müllegger

Gabriel Hurley

unread,
Jun 24, 2011, 4:30:37 PM6/24/11
to django-d...@googlegroups.com
I just want to quickly add a second mention for the importance of being able to control row-level groupings of fields, as well as row-level attributes such as classes. It's a problem I've run into many times and would love to have included in the new form-rendering solution.

All the best,

    - Gabriel

Bruno Renié

unread,
Jun 25, 2011, 6:05:58 AM6/25/11
to django-d...@googlegroups.com
Hi all,

Someone did a couple of benchmarks to measure the performance impact
of my patch (#15667, the template-widgets implementation aka
django-floppyforms). There is some slowdown, the question is whether
it's acceptable or not, and where's the limit. If you have simple
forms, they'll render in 6 milliseconds instead of 3.5. However, with
fields with lots of choices, this gets much worse. Something that
takes 32 milliseconds with the current forms implementation can take
almost 200 milliseconds to render if it's done in the templates. The
biggest part of this time is spent actually rendering the templates,
not loading them. So template caching helps but only to a limited
extent.

Also see on the ticket page, there are a couple of pretty graphs that
show exactly where time is spent. For instance, 12% of the rendering
time is spent doing isinstance() calls, that's part of the template
rendering logic.

I did the same benchmarks with pypy, and the results are interesting.
Raw results are here, for some context see the files attached to the
ticket:

http://dpaste.com/hold/558425/

So, currently I see how it can be a problem to have this as the only
widget rendering system. I don't think it's worth maintaining both
implementation (the current one and the template one) with ways to
switch from one way to another, it's going to be too much of a burden
to maintain.

When the template language gets faster, maybe we can integrate my
patch. The question is, what slowdown is acceptable? Or are we
expecting a speedup? With jinja2, widget rendering is actually
*faster* using templates than using the current string-based
implementation but I'm not sure we can go this far with django
templates. So:

* We need faster templates for this to land in trunk
* We need pypy :)
* If you want template-base widgets *now*, use django-floppyforms.
* If you want to use the new forms / templates API as soon as it's
done… how do you do it? Is it going to be packaged as an app, as a
patched version of django?

That being said, I really like the design work that's been done and I
look forward to trying it.

-Bruno

> --
> You received this message because you are subscribed to the Google Groups "Django developers" group.

Benoît Bryon

unread,
Jun 26, 2011, 9:31:31 AM6/26/11
to django-d...@googlegroups.com
Hello,

Le 25/06/2011 12:05, Bruno Reni� a �crit :


> * If you want template-base widgets *now*, use django-floppyforms.
> * If you want to use the new forms / templates API as soon as it's

> done� how do you do it? Is it going to be packaged as an app, as a
> patched version of django?

One option could be to use django-formrenderingtools and adapt it to the proposed API.
Formrenderingtools does not cover exactly the proposed API yet, but I guess it could.
In fact, formrenderingtools will be deprecated in next Django releases. So there is no interest in maintaining it "as is". Offering backward compatibility for the form rendering API may be the best way to go.

The main repository is https://bitbucket.org/benoitbryon/django-formrenderingtools
I just pushed it to github too (let's try hg-git) : https://github.com/benoitbryon/django-formrenderingtools

Forks are welcome!

This is only a suggest, so if most of you think we'd better fork another app or build a brand one... please let me know. I would stop committing on formrenderingtools and contribute to the other app instead ;)

Benoit

Gregor Müllegger

unread,
Jun 26, 2011, 2:31:06 PM6/26/11
to django-d...@googlegroups.com
Hi Benoit, hi Bruno,

2011/6/26 Benoît Bryon <ben...@marmelune.net>:
> Hello,


>
> Le 25/06/2011 12:05, Bruno Renié a écrit :
>>
>> * If you want template-base widgets *now*, use django-floppyforms.
>> * If you want to use the new forms / templates API as soon as it's

>> done… how do you do it? Is it going to be packaged as an app, as a


>> patched version of django?
>
> One option could be to use django-formrenderingtools and adapt it to the
> proposed API.
> Formrenderingtools does not cover exactly the proposed API yet, but I guess
> it could.
> In fact, formrenderingtools will be deprecated in next Django releases. So
> there is no interest in maintaining it "as is". Offering backward
> compatibility for the form rendering API may be the best way to go.
>
> The main repository is
> https://bitbucket.org/benoitbryon/django-formrenderingtools
> I just pushed it to github too (let's try hg-git) :
> https://github.com/benoitbryon/django-formrenderingtools
>
> Forks are welcome!
>
> This is only a suggest, so if most of you think we'd better fork another app
> or build a brand one... please let me know. I would stop committing on
> formrenderingtools and contribute to the other app instead ;)
>
> Benoit

If the need for the new form rendering is big enough or requested by lots of
people (I think that criteria is already matched :-)), will I port the API to
an external re-usable library. However I would create a new library that only
contains the things that will be included in a future django version so that
upgrading from that library to the django-builtin form rendering is as
painless as possible.

--
Servus,
Gregor Müllegger

Chris Beaven

unread,
Jun 26, 2011, 10:33:25 PM6/26/11
to django-d...@googlegroups.com
How do I override a field's label or help text?
Specifically, help text may need to look something like: [[Can this person <a href="{% url help-managers %}" title="What's a manager?">manage</a> {{ site.name }}?]]

How are HTML classes specified for rows which are required / contain errors?

(and one more slightly obscure one, probably out of scope...)
How does a row know whether it contains HTML block elements?
For example, a "p" row needs to render differently if it contains HTML block elements, such as a field represented as an unordered list of checkboxes.

Chris Beaven

unread,
Jun 26, 2011, 10:53:26 PM6/26/11
to django-d...@googlegroups.com
Oh, and one more critical one:

How does the form in python have knowledge of the widget which the field was rendered with as picked by the template?
This is critical since building the form's data requires using the widget's value_from_datadict.

Gregor Müllegger

unread,
Jun 28, 2011, 7:56:41 AM6/28/11
to django-d...@googlegroups.com
Hi Chris,

2011/6/27 Chris Beaven <smile...@gmail.com>:


> How do I override a field's label or help text?
> Specifically, help text may need to look something like: [[Can this person
> <a href="{% url help-managers %}" title="What's a manager?">manage</a> {{
> site.name }}?]]

This isn't addressed with the proposed template tags. You can still write out
a single row by hand if you need to tweak it in the low level details. I know
this is not ideal and exactly what we want to avoid with the new rendering.
Because of this I will try to design the code that spits out the form in the
end to be very modular. Basically it should be possible in the end to have
your own "formconfig" template tags that change the rendering a bit, like
changing a label or the help text (label is also my sample usecase).

However I think these templatetags could go into a thirdparty app. First
reason is to show-case that thirdparty rendering modifications are possible,
second to not clutter the builtin tags with too many possibilities. I would
like more to provide a framework for rendering, than all the tiny details you
propably could plugin into it your self.

BTW: even without the thirdparty apps it's already possible to change the
label for a field in an (somehow) easy way. Just extend from the row template
you use for your other rows in the form and override the {% block label %}.

Another option is to have something like this in your row template:

{% firstof label field.label %}

label is usually not in the row's scope so field.label is choosen. Now you can
modify the label with:

{% formrow myform.field with label="My new label" %}

> How are HTML classes specified for rows which are required / contain errors?
> (and one more slightly obscure one, probably out of scope...)

This can be achieved in the row template:

<div{% if field.errors %} class="errors"{% endif %}>

> How does a row know whether it contains HTML block elements?
> For example, a "p" row needs to render differently if it contains HTML block
> elements, such as a field represented as an unordered list of checkboxes.

This is not addressed, and I have no clue how we should do this -- or if we
should do this at all :o)

2011/6/27 Chris Beaven <smile...@gmail.com>:

I think thats conceptually not possible. We simply can change the widget during
template rendering time, which makes it impossible to decide in the python
code with which widget we end up. And theoretically we could even render the
form twice with different widgets. Or throw the rendered template away without
using the widget at all.

So I think we must make clear that the used widgets must be somehow
compatible. I agree that we need to document this in one or the other way.

--
Servus,
Gregor Müllegger

Chris Beaven

unread,
Jun 28, 2011, 9:43:56 PM6/28/11
to django-d...@googlegroups.com


On Tuesday, June 28, 2011 11:56:41 PM UTC+12, Gregor Müllegger wrote:

However I think these templatetags could go into a thirdparty app.

-0, changing a label / help text at least are pretty common cases - the template designer shouldn't be at the mercy of the python form settings.
I'm glad that you've thought about this a bit though and agree that just getting something working is better than trying to fix all the problems at once.
 

BTW: even without the thirdparty apps it's already possible to change the
label for a field in an (somehow) easy way. Just extend from the row template
you use for your other rows in the form and override the {% block label %}.

A passing thought is that I wonder if we allow for this inline too, like how {% form using  %} works...

> How are HTML classes specified for rows which are required / contain errors?

> (and one more slightly obscure one, probably out of scope...)

This can be achieved in the row template:

<div{% if field.errors %} class="errors"{% endif %}>

Can't a row technically have more than one field though? I guess it's still solvable with a custom template filter or the like, just seems like another common case we could account for with some context var passed to the row template.
 

> How does the form in python have knowledge of the widget which the field was

> rendered with as picked by the template?
> This is critical since building the form's data requires using the widget's
> value_from_datadict.

I think thats conceptually not possible. We simply can change the widget during
template rendering time, which makes it impossible to decide in the python
code with which widget we end up. And theoretically we could even render the
form twice with different widgets. Or throw the rendered template away without
using the widget at all.


In this case, why do we even have this format: {% formconfig widget widgets.Textarea for "comment" %} and the terminology of widgets?
It seems like that should really just boil down to {% formconfig field using "some/textarea.html" for "comment" %} and just referring to fields only. A widget to me encompasses the backend logic of decoding the data, etc.

I think it'd be a great plus if we *could* make it work for different widgets. This would really give power to change forms dynamically at the template layer.
I remember thinking a while ago about some kind of widget repository available to the templates, combined with a hidden input per field that could notify the python form of the alternate widget used (the hidden input only used if the widget differed than the default for that field). This would allow for the same form to be used with different templates which used completely different widgets.

Gregor Müllegger

unread,
Jul 8, 2011, 8:19:44 AM7/8/11
to django-d...@googlegroups.com
Hi Chris,

2011/6/29 Chris Beaven <smile...@gmail.com>:


>>
>> I think thats conceptually not possible. We simply can change the widget
>> during
>> template rendering time, which makes it impossible to decide in the python
>> code with which widget we end up. And theoretically we could even render
>> the
>> form twice with different widgets. Or throw the rendered template away
>> without
>> using the widget at all.
>
> In this case, why do we even have this format: {% formconfig widget
> widgets.Textarea for "comment" %} and the terminology of widgets?
> It seems like that should really just boil down to {% formconfig field using
> "some/textarea.html" for "comment" %} and just referring to fields only. A
> widget to me encompasses the backend logic of decoding the data, etc.
> I think it'd be a great plus if we *could* make it work for different
> widgets. This would really give power to change forms dynamically at the
> template layer.
> I remember thinking a while ago about some kind of widget repository
> available to the templates, combined with a hidden input per field that
> could notify the python form of the alternate widget used (the hidden input
> only used if the widget differed than the default for that field). This
> would allow for the same form to be used with different templates which used
> completely different widgets.

So I spoke to Carl yesterday about some of the ideas you brought up and we
agreed on how we want to handle them :-)


You've mentioned if being able to change the widget in the template it would
be cool to let the python source also know which widget should then be used to
parse the data. My concern about this was that an HTML data value should not
be allowed to exchange code parts on the server. Sure we would limit that to
specific widget classes, but with that power in HTML you would be able to
break the server side code, either by being evil or not understanding widgets
in total.
The same of course is true if we just change the widget without letting the
python form know what is happening, this makes it also possible to break the
server … we talked then about what a widget actually is: It has two purposes,
first is parsing the data making it usable for the field, second is
representational -- rendering an HTML input that is compatible with the
widget's data format.

So we decided to skip changing a widget totally in the form rendering.
Displaying a form and anything else that happens in the template has a
representational purpose, so we saw it would be out of scope for the project.
However changing the template that is used to render a widget will of course
still be possible (also passing in extra arguments etc.)


Now that we drop the idea of exchanging widgets we also don't longer need the
widgets template variable that holds the possible widget implementations you
can drop in. But the formfields var would be left to decide cases like:

{% formconfig widget using "textarea.html" for formfields.CharField %}

We decided to drop that variable as well, replacing it with a string that can
match the class name of a field:

{% formconfig widget using "textarea.html" for "CharField" %}

We justified it because this concept of matching a string to python structure
already exists. Examples are {% load mytemplatelib %} that loads a file named
after the argument, or some app.Model arguments used in third party libs.
There might be the concern that "CharField" is ambiguous if some else also
implements a "CharField". But this might be even a feature … if someone
implements also a CharField it might be very similar to django's built-in
one, otherwise you would name it differently.

btw I also plan to use "field" as argument for formconfig instead of "widget"
to match more the {% form[row|field] %} tags:

{% formconfig field using "textarea.html" with placeholder="type here
…" for "CharField" %}


Now to your django-forms implementation that has the "extends" argument for
the {% form %} tag. I still totally like the idea behind that, but like I
already said in the other message -- it might be confusing to have two
different meanings of the block tag. Carl has the same opinion here so we
won't include that into our implementation.


Thanks a lot again for your input, it created some vital think processes for
me and resulted in good improvements on the proposal (hope you see it the
same). Keep posting.

--
Servus,
Gregor Müllegger

Chris Beaven

unread,
Jul 8, 2011, 6:00:19 PM7/8/11
to django-d...@googlegroups.com


On Saturday, July 9, 2011 12:19:44 AM UTC+12, Gregor Müllegger wrote:

[...] So we decided to skip changing a widget totally in the form rendering.


Displaying a form and anything else that happens in the template has a
representational purpose, so we saw it would be out of scope for the project.
However changing the template that is used to render a widget will of course
still be possible (also passing in extra arguments etc.)


Fair enough, I understand the concern with giving HTML power over choice of rendered widget (not sure I totally agree, but concede that it's easier to not have to worry about it).

I guess this means that rendering a field as "hidden" in the template is also out, since this needs to modify more than just the field's representation (specifically, non_field_errors).


Now that we drop the idea of exchanging widgets we also don't longer need the
widgets template variable that holds the possible widget implementations you
can drop in. But the formfields var would be left to decide cases like:

{% formconfig widget using "textarea.html" for formfields.CharField %} 

 [or]

{% formconfig widget using "textarea.html" for "CharField" %}

I'm not sure I see the importance of overriding a widget's template, to be honest. IMO it seems much more likely that you'll be worried with alternate rendering for different fields rather than all widgets.
 

We justified it because this concept of matching a string to python structure
already exists. Examples are {% load mytemplatelib %} that loads a file named
after the argument, or some app.Model arguments used in third party libs.

That's a bit of a far-reaching justification, but I'm indifferent about the whole widget template bit of the proposal, so roll with whatever makes most sense.
 

btw I also plan to use "field" as argument for formconfig instead of "widget"
to match more the {% form[row|field] %} tags:

{% formconfig field using "textarea.html" with placeholder="type here
…" for "CharField" %}

I worry that we're introducing two meanings of what 'for' represents. Again, it seems to make more sense in my mind that it'd be in context of a field, not a widget.
 

Now to your django-forms implementation that has the "extends" argument for
the {% form %} tag. I still totally like the idea behind that, but like I
already said in the other message -- it might be confusing to have two
different meanings of the block tag. Carl has the same opinion here so we
won't include that into our implementation.

(Note that 'extends' also applies the formconfig, row and field tags too)

I understand the potential for confusion, but I think it really does bring something big to the table which is unfortunate to miss out on.
For example, if I just want to customize one field template on a form then without this I have to redefine the whole form template.
It also allows for complex redefinition of a field's label and help text without having to redefine the entire field template.

We're already bringing a new concept of inline sub-template definition via the 'using' argument.
I don't see that it's much more of a push to simultaneously introduce inline sub-template extension via the 'extends' argument.


Here's real-life example off the top of my head we had the other day. A client had a multi-step registration form. Usually, our rows show a * next to the label of required fields but all of the first step was required so the client didn't want the stars to show. I envision this would look like this in django-forms (lets assume we've customised 'forms/field/base.html' to conditionally conditionally change the label class or just conditionally add the * after it):

{% form form extends "forms/p.html" %}
    {% block config %}
        {% formconfig field with required=0 %}
    {% endblock %}
{% endform %}

What would this look like with the current form proposal?

Carl Meyer

unread,
Jul 8, 2011, 6:48:30 PM7/8/11
to django-d...@googlegroups.com
Hi Chris,

On 07/08/2011 04:00 PM, Chris Beaven wrote:


> On Saturday, July 9, 2011 12:19:44 AM UTC+12, Gregor M�llegger wrote:
> [...] So we decided to skip changing a widget totally in the form
> rendering.
> Displaying a form and anything else that happens in the template has a
> representational purpose, so we saw it would be out of scope for the
> project.
> However changing the template that is used to render a widget will
> of course
> still be possible (also passing in extra arguments etc.)
>
>
> Fair enough, I understand the concern with giving HTML power over choice
> of rendered widget (not sure I totally agree, but concede that it's
> easier to not have to worry about it).

We can always revisit additional features later if needed, but scope
creep is a GSoC-killer.

I think most of the use cases for replacing widgets in the template are
really purely representational, they don't change the server-side data
handling at all. In cases where it would, the existing proposal was
already inadequate. And the majority of cases, when it wouldn't, can be
handled just by swapping in a different template; there's no need to
care about the widget class. If the form officially is using a TextInput
widget on the Python side and you swap in a template for that field that
renders a select dropdown, the Python code won't care; it still gets a
name and a value in request.POST.

> I guess this means that rendering a field as "hidden" in the template is
> also out, since this needs to modify more than just the field's
> representation (specifically, non_field_errors).

Yes. I mean, you're fully free to render a field as hidden yourself (via
template), but anytime you make a change in the HTML that implies any
change in server-side data handling, you're on your own to figure out
the right solution for your case (which is no different than now).

> Now that we drop the idea of exchanging widgets we also don't longer
> need the
> widgets template variable that holds the possible widget
> implementations you
> can drop in. But the formfields var would be left to decide cases like:
>
> {% formconfig widget using "textarea.html" for formfields.CharField %}
>
> [or]
>
> {% formconfig widget using "textarea.html" for "CharField" %}
>
> I'm not sure I see the importance of overriding a widget's template, to
> be honest. IMO it seems much more likely that you'll be worried with
> alternate rendering for different fields rather than all widgets.

Seems like there might be a small misunderstanding here. The shown
syntax is for overriding the widget template used for any instances of a
given _field_ type, not a given _widget_ type. I agree that a more
common use case is probably changing things up for a particular field in
your form, but having this allows the possibility of reusable form
layouts that can do interesting things with representations of
particular field types.

> btw I also plan to use "field" as argument for formconfig instead of
> "widget"
> to match more the {% form[row|field] %} tags:
>
> {% formconfig field using "textarea.html" with placeholder="type here

> �" for "CharField" %}


>
> I worry that we're introducing two meanings of what 'for' represents.
> Again, it seems to make more sense in my mind that it'd be in context of
> a field, not a widget.

I think it's actually pretty intuitive in practice that the operand of
"for" can be either the name of a specific field, or the name of a field
class. Ambiguity isn't an issue; specific field names take precedence
(and capitalization conventions should prevent ambiguity anyway). And
like I said above, I think the latter is useful for reusable form layouts.

> Now to your django-forms implementation that has the "extends"
> argument for
> the {% form %} tag. I still totally like the idea behind that, but
> like I
> already said in the other message -- it might be confusing to have two
> different meanings of the block tag. Carl has the same opinion here
> so we
> won't include that into our implementation.
>
> (Note that 'extends' also applies the formconfig, row and field tags too)
>
> I understand the potential for confusion, but I think it really does
> bring something big to the table which is unfortunate to miss out on.
> For example, if I just want to customize one field template on a form
> then without this I have to redefine the whole form template.
> It also allows for complex redefinition of a field's label and help text
> without having to redefine the entire field template.

Why would you otherwise have to redefine the whole form template? A form
template is a normal template; you can put blocks in it and have another
form template extend it and override those blocks. I don't think it's
onerous (in fact, I think it's clearer and more maintainable) to do this
in a separate file rather than "inline" as your proposal does. And I'm
strongly in favor of reusing existing template language features and
conventions rather than inventing new and confusing syntax for "inline
extends"; IMO redefining the meaning of {% block %} contextually is
simply a non-starter due to the extra difficulty in quickly
comprehending template structure.

AFAICT, your proposal doesn't actually bring any new capabilities to the
table, it just lets you do the same thing inline rather than in a
separate form template file that you reference with "using".

> Here's real-life example off the top of my head we had the other day. A
> client had a multi-step registration form. Usually, our rows show a *
> next to the label of required fields but all of the first step was
> required so the client didn't want the stars to show. I envision this
> would look like this in django-forms (lets assume we've customised
> 'forms/field/base.html' to conditionally conditionally change the label
> class or just conditionally add the * after it):
>
> {% form form extends "forms/p.html" %}
> {% block config %}
> {% formconfig field with required=0 %}
> {% endblock %}
> {% endform %}
>
> What would this look like with the current form proposal?

You would just create a template "forms/p-no-stars.html" (or whatever)
that {% extends "forms/p.html" %} and overrides the config block in the
same way, and then you'd just say {% form form using
"forms/p-no-stars.html" %}.

Carl

Chris Beaven

unread,
Jul 9, 2011, 4:50:50 AM7/9/11
to django-d...@googlegroups.com
Thanks for the followup reply, Carl.

Yes, I think I was a bit confused regarding "for". Sounds fine.
Your points about scope creep and keeping the proposal as achievable as possible is also noted.

If we're going to keep things simple, why are we introducing the idea of inline "using" templates?

You go a long way of convincing me that the confusion introduced by 'extends' isn't worth it.
You're right, my example suited a separate template file just fine. But do I really need to create a new template file for all specific cases? It seems like the following templates would be much better to just be kept inline with the main template the form is being used in.

"forms/p-custom-help-text-for-displayname.html"
{% extends "forms/p.html" %}
{% block config %}
{% formconfig field using "checkbox-custom-help-text-for-displayname.html" for form.display_name %}
{% endblock %}

"forms/fields/checkbox-custom-help-text-for-displayname.html"
{% extends "forms/fields/checkbox.html" %}
{% block label %}Show posts by me as from <em>{{ user.username }}</em> rather than <em>{{ user.get_full_name }}</em>{% endblock %}

Perhaps I'm not quite getting how we'd do this kind of thing with the current proposal.

Carl Meyer

unread,
Jul 9, 2011, 12:48:08 PM7/9/11
to django-d...@googlegroups.com
Hi Chris,

On 07/09/2011 02:50 AM, Chris Beaven wrote:
> If we're going to keep things simple, why are we introducing the idea of
> inline "using" templates?

That's a good question. I wouldn't be gutted at all if we dropped
inline-using from the initial scope, too, because I really think
separate template files is not a bad thing. But I'm also not as
concerned about having it in because in my mind it really doesn't have
the complexity downsides of inline-extends; its just a pretty normal
scoped tag, much like "with" or other existing tags.

> You go a long way of convincing me that the confusion introduced by
> 'extends' isn't worth it.
> You're right, my example suited a separate template file just fine. But
> do I really need to create a new template file for all specific cases?
> It seems like the following templates would be much better to just be
> kept inline with the main template the form is being used in.
>
> "forms/p-custom-help-text-for-displayname.html"
> {% extends "forms/p.html" %}
> {% block config %}
> {% formconfig field using
> "checkbox-custom-help-text-for-displayname.html" for form.display_name %}
> {% endblock %}
>
> "forms/fields/checkbox-custom-help-text-for-displayname.html"
> {% extends "forms/fields/checkbox.html" %}
> {% block label %}Show posts by me as from <em>{{ user.username }}</em>
> rather than <em>{{ user.get_full_name }}</em>{% endblock %}
>
> Perhaps I'm not quite getting how we'd do this kind of thing with the
> current proposal.

The only issue I see here is naming :-) When a template gets that
specific in purpose, it doesn't make sense to pretend its reusable and
try to name it in a "generic" way. In general I don't see any problem
with making specific included form templates for specific needs. I'd
probably just call these "profile_form.html" and "display_name.html", if
those are names that make sense in the context of the project. I don't
know about your projects, but mine already include plenty of template
partials with very project-specific, non-generic purposes, so I don't
see this as that different.

Carl

Gregor Müllegger

unread,
Jul 11, 2011, 12:17:08 PM7/11/11
to django-d...@googlegroups.com
Hi Chris, thanks Carl,

2011/7/9 Carl Meyer <ca...@oddbird.net>:


> Hi Chris,
>
> On 07/08/2011 04:00 PM, Chris Beaven wrote:

>> ...


>> I guess this means that rendering a field as "hidden" in the template is
>> also out, since this needs to modify more than just the field's
>> representation (specifically, non_field_errors).
>
> Yes. I mean, you're fully free to render a field as hidden yourself (via
> template), but anytime you make a change in the HTML that implies any
> change in server-side data handling, you're on your own to figure out
> the right solution for your case (which is no different than now).

Hm, hidden fields are something that I have not taken into account after our
latest iteration. I think they are a bit special-cased and might need
attention even if we drop changing-the-widget feature. I think it is also
technically something different than just exchanging widgets since any form
field is already aware about how it can be rendered with a hidden widget. That
also solves the don't-break-my-python-from-the-template issue since we only
use the hidden widget which is supported by the normal form field. See the
"hidden_widget" attributes:

https://code.djangoproject.com/browser/django/trunk/django/forms/fields.py#L45

> ...

Your example is already achieveable without introducing a new template:

{% form form using %}


{% formconfig field with required=0 %}

{% include "forms/p.html" %}
{% endform %}

This of course only works since you don't want to change actual markup from
"forms/p.html". Otherwise you must define a new template.

And because the following examples are the same ...

{% form myform using "forms/p.html" %}
{% form myform using %}{% include "forms/p.html" %}{% endform %}

... is also my justification for having the second one ("using" inline)
directly in the GSoC scope. "extends" is IMHO something very different, like
Carl said, because it introduces a new idiom that is not already present in
the current features of the template language.

--
Servus,
Gregor Müllegger

Chris Beaven

unread,
Jul 11, 2011, 7:15:00 PM7/11/11
to django-d...@googlegroups.com


On Tuesday, July 12, 2011 4:17:08 AM UTC+12, Gregor Müllegger wrote:

Hm, hidden fields are something that I have not taken into account after our
latest iteration. I think they are a bit special-cased and might need
attention even if we drop changing-the-widget feature.

Agreed, it'd be nice to make this case work, and like you say, we have hidden_widget on the field already - just need a way to activate it (it's probably just as simple as a formconfig which changes the form field's widget attribute to hidden_widget).
 

Your example is already achieveable without introducing a new template:

    {% form form using %}
        {% formconfig field with required=0 %}
        {% include "forms/p.html" %}
    {% endform %}

This of course only works since you don't want to change actual markup from
"forms/p.html". Otherwise you must define a new template.

Yeah, you're (both) right. I'm happy enough to see extends dropped for the sake of simplicity. It was me overthinking the situation.
And yes, since you've shown an advantage of the inline using, it makes sense to keep.
Reply all
Reply to author
Forward
0 new messages