Hi,
like you might know I've prepared as pre-work to my GSoC project a repository
[1] with examples for two different approaches to my upcoming work on the form
rendering. The first approach is called "single_tag" [2] the second one
"modifier_tags" [3]. I agreed with my mentor Carl that we will follow a
"hybrid" style of both proposals [4].
I tried here to summarize a bit how the new tags will look like. I call now
for comments and will really appreciate any feedback on them. Being it about
their naming or if you have ideas for other tags. Be picky, this is something
lots of people will use -- and though we want it to be as easy as possible to
get started with.
::
{% renderform <form instance> [<field name> ...] [hidden <field
name> ...] [exclude <field name> ...] %}
The renderform tag renders the complete form::
{% renderform my_form %}
You can influence which fields will be used, sparing some fields out
if you want that. Examples::
{% renderform my_form "username" "password" %}
Renders only my_form.username and my_form.password and ignoring all other
fields on the form. ::
{% renderform my_form exclude "first_name" "last_name" %}
Render all fields but my_form.first_name and my_form.last_name. ::
{% renderform my_form hidden "honeypot" %}
Render all fields but outputs the my_form.honeypot field as a hidden field.
Thats it in what the renderform takes on arguments. You can influence it's
rendering behaviour in more detail by using *form modifier tags*.
One such modifier tag is the formlayout tag::
{% formlayout <layout name> %}
Every {% renderform %} that is following in the templates will use the
specified layout for rendering. We will provide layouts called "table", "ul"
and "p". Users can specify new layouts just by putting some templates in the
right template path.
The {% form %} tag is limiting the scope of these modifier tags. If a modifier
tag is wrapped in such a form-block, then it will lose its influence on form
rendering outside of the form tag. ::
{% form %}
{% formlayout "table" %}
{% renderform my_form %} {# will use table layout #}
{% endform %}
{% renderform my_form %} {# will use default layout #}
The description of the form tag implies that the modifier tags are able to
set/modify the global state in the current template. That is something that is
explicitly wanted. This way you can set a "formlayout" in the head of your
base template and any other extending template will have this default for the
form rendering.
Another tag to modify the rendering is the widget tag::
{% widget [<widget class or instance>] [using <template name>] for
<field type> [with <varname>=<varvalue> ...] %}
In this syntax description <field type> means that you can specify one of
three things as argument:
1. A bound field, means that the widget will be rendered only for this field.
2. A field class/type that will match for all fields that are using this
formfield class. Example::
{% widget widgets.PasswordInput for formfields.CharField %}
will render a <input type="password" /> for all charfields.
3. A field name (string). This will apply to all fields that have that
particular field name. It's only useful if the option should apply to more
than one form or as convenience for short reference of a field.
Some examples::
{% widget widgets.Textarea for my_form.comment %} (1. case)
{% widget widgets.DatePicker for formfields.DateField %} (2. case)
{% widget widgets.PasswordInput for "password" %} (3. case)
You can also change the template that will be used to render the widget with
the using keyword (we assume at this point that until this GSoC project is
finished we will likely have template based widget rendering like currently
developed by Bruno [5]), an example::
{% widget using "my_textarea_widget.html" for my_form.comment %}
It's actually possible to specify a special template for the widget *and* to
change the widget class itself with the tag::
{% widget widgets.DatePicker using
"widgets/alternative_datepicker.html" for my_form.birthday %}
The "with varname=varvalue" bit in the widget tag is meant as possibility to
pass extra arguments into the template that will be used to render the widget.
This will use the same syntax as django's ``include`` tag [6]::
{% widget for my_form.text with rows=10 cols=20 %}
{% widget using "widgets/tinymce.html" for my_form.comment with
theme="advanced" %}
At the end a short word to the meanings of widgets.Textarea etc. This
will basically be a template variable referencing the Textarea widget. So we
don't use special syntax for this in the tag, we just pull out the "widgets"
template variable that will be passed in via a context processor.
The "widgets" and "formfields" variables will be modifiable by users, so that
they can register their own widgets in their reusable apps, then usable in all
templates.
Thanks if you have read so far. Now please start commenting :-)
Gregor
| [1] https://github.com/gregmuellegger/django-form-rendering-api
| [2] https://github.com/gregmuellegger/django-form-rendering-api/blob/master/formapi/templates/api_samples/single_tag.html
| [3] https://github.com/gregmuellegger/django-form-rendering-api/blob/master/formapi/templates/api_samples/modifier_tags.html
| [4] https://github.com/gregmuellegger/django-form-rendering-api/blob/master/formapi/templates/api_samples/hybrid.html
| [5] http://code.djangoproject.com/ticket/15667
| [6] http://docs.djangoproject.com/en/dev/ref/templates/builtins/#std:templatetag-include
I like this. Simple, covers all the common use cases that I can see.
My only feedback here would be mostly bikeshedding -- the fact that {%
form %} is a configuration action, and {% renderform %} is the
rendering action. It feels to me like {% form %} should be the
rendering action, and {% formconfig %} (or something similar) should
be the configuration action.
> Another tag to modify the rendering is the widget tag::
>
> {% widget [<widget class or instance>] [using <template name>] for
> <field type> [with <varname>=<varvalue> ...] %}
>
> In this syntax description <field type> means that you can specify one of
> three things as argument:
>
> 1. A bound field, means that the widget will be rendered only for this field.
> 2. A field class/type that will match for all fields that are using this
> formfield class. Example::
>
> {% widget widgets.PasswordInput for formfields.CharField %}
>
> will render a <input type="password" /> for all charfields.
>
> 3. A field name (string). This will apply to all fields that have that
> particular field name. It's only useful if the option should apply to more
> than one form or as convenience for short reference of a field.
Broadly, I like what you've described here for the widget field. My
only concern is a subtle one, regarding the way that quotes are
interpreted.
Over time, Django's template language has been slowly moving to a
position where any user-specified argument can be interpreted as a
variable or as a constant, with quotation used to differentiate
between the two. For example, the {% url %} tag was modified in 1.3
(using the future import syntax) so that the argument is interpreted
literally if it is quoted, an as a variable if it isn't. This opens up
all sorts of options for dynamic template programming, making the
decision of where a link will land based on view logic, rather than
template logic. Looking at your examples:
> {% widget widgets.Textarea for my_form.comment %} (1. case)
> {% widget widgets.DatePicker for formfields.DateField %} (2. case)
> {% widget widgets.PasswordInput for "password" %} (3. case)
In these examples, for X is being interpreted as a context variable in
case 1, an interpreted class name in case 2, and a string that will
match against a name in case 3.
* Case 1, but determining in the view which field will be a text area.
* Case 2, but determining the class that will be matched in the view.
* Case 3, but matching against a dynamic string (i.e., determine in
the view the string that will be used for a match).
Now, this isn't a case where I have an obvious use case I can point at
-- these examples feel contrived, even to me. The third case is the
only one that seems likely in practice. What I'm really arguing for
here is consistency with the broad direction of the template language
as a whole -- if you're specifying a constant, it should be quoted.
Other than these relatively minor points, this looks really good to me!
Yours,
Russ Magee %-)
I'm not sure I am convinced. It seems to me that there would be three
common use cases for form rendering:
1) Render this form using defaults
2) Render most forms using the defaults, but render for XXX using layout YYY
3) Render all forms on this form using YYY
It seems weird to me that in order to hit use case 3, you need to wrap
your entire template in a {% form %} block. Having a global form
layout context so that {% formlayout %} will work without an
encompassing {% form %} block makes more sense to me.
Yours,
Russ Magee %-)
On 05/22/2011 07:18 PM, Russell Keith-Magee wrote:
> I like this. Simple, covers all the common use cases that I can see.
> My only feedback here would be mostly bikeshedding -- the fact that {%
> form %} is a configuration action, and {% renderform %} is the
> rendering action. It feels to me like {% form %} should be the
> rendering action, and {% formconfig %} (or something similar) should
> be the configuration action.
Hmm. In the current proposal, {% form %} isn't really an action at all,
it's just a scope. Which is why a generic name seemed sensible for it.
Whereas {% renderform %} is an action - it renders the form - and it
seemed good for its name to be explicit about that action.
I don't care too much about that naming, though. If the "common case" is
just {% renderform my_form %} with no enclosing configuration scope at
all, it would be nice to keep that common case as short as possible.
Oh, I'm all about maintaining our newfound consistency in template tag
argument quoting. Which is why that was part of my feedback to the
original proposals, and Gregor and I spent quite a while working out the
solution which you apparently read a bit too quickly to notice ;-)
If you read again, you'll note that "widgets" and "formfields" are not
some kind of magical syntactical marker, they are just template
variables (dictionaries of widgets and formfields, respectively) that
would be provided by a new context processor. So in every case above,
the argument is either a quoted string or a normal template variable;
there are no syntactic special cases.
Our idea was that there would be public API for adding custom widgets
and formfields into these dictionaries, so that a third-party app can
make its own custom widgets and fields available for use in your
templates without requiring you to add yet another context processor.
I'm realizing now that we need to think carefully about the implications
of introducing a global flat namespace for widgets and formfields like
this; it could cause some nasty bugs if apps are stomping on each
others' names, or even built-in field/widget names. Maybe requiring an
additional context processor for any third-party app that wants to
provide custom fields and widgets into the template context isn't such a
bad idea after all.
Carl
I guess I was comparing
{% form %}
{% renderform myform %}
{% endform %}
to
{% form "table %}
{% renderform myform %}
{% endform %}
and thinking the latter didn't seem too comparatively onerous, even if
you were doing it for every form render. But I'd forgotten that for
simple cases you could otherwise just do {% renderform myform %} with no
block tag; it is unfortunate to require the block tag every time in case 3.
For the case-by-case override, though, I'd still much rather write
{% form "table" %}
{% renderform myform %}
{% endform %}
than
{% form %}
{% formlayout "table" %}
{% renderform myform %}
{% endform %}
What if instead of allowing form modifier tags to appear unenclosed, and
making them then implicitly global, we had a {% formdefaults %} tag that
paralleled the {% form %} tag, except it defined your defaults for form
rendering:
{% formdefaults "table" %}
{% widget ... %}
{% endformdefaults %}
This is much more explicit, which means that a) a random new designer
reading your templates is more likely to notice that global defaults are
being defined, and b) you're less likely to accidentally define global
defaults because you omitted an enclosing block tag.
Global state is serious business - it means action-at-a-distance. It
should be obvious when it's happening, and hard to do accidentally.
Carl
Preface: This is 100% bikeshed, you/Gregor get to paint it, and I
won't be put out at all if it goes a different way.
My argument: I'm trying to think of another example in Django's
template language where the template tag is an "action" in this way.
To my reading, outside of the tags used for logic (for, if, etc), and
tags that define a contextual block (autoescape, comment, etc),
template tags have the flavor of {% this block will be replaced with X
%}, not {% do X here %}. For example, it's {% csrf_token %}, not {%
render_csrf_token %}; {% cycle %}, not {% render_cycle_value %}; and
so on.
I saw that explanation; I just wasn't sure how it would play out in
practice. In particular, I wasn't sure how:
{% widget widgets.PasswordInput for foo %}
would be interpreted, since foo could be a string, a field, or a
widget class; or how
{% widget widgets.PasswordInput for "foo" %}
would be interpreted as anything other than a string (i.e., is there a
"constant" interpretation for the first two use cases).
Is the intention to run make the interpretation of {% widget for %}
argument based on a type check (i.e., isinstance(var, basestring) ->
field named var, isinstance(var, Field) -> field var, issubclass(var,
Field) -> fields of type var) of the context variable that is
provided?
> Our idea was that there would be public API for adding custom widgets
> and formfields into these dictionaries, so that a third-party app can
> make its own custom widgets and fields available for use in your
> templates without requiring you to add yet another context processor.
> I'm realizing now that we need to think carefully about the implications
> of introducing a global flat namespace for widgets and formfields like
> this; it could cause some nasty bugs if apps are stomping on each
> others' names, or even built-in field/widget names. Maybe requiring an
> additional context processor for any third-party app that wants to
> provide custom fields and widgets into the template context isn't such a
> bad idea after all.
Makes sense to me.
Yours,
Russ Magee %-)
My counterargument would be to ask what other configuration items
there are -- or might there be in the future. Keeping the {%
formlayout %} tag doesn't seem especially onerous to me; it's explicit
about what is being configured; and it allows for future expansion in
the case that we think of some other configuration item that could be
handled at a form level.
> What if instead of allowing form modifier tags to appear unenclosed, and
> making them then implicitly global, we had a {% formdefaults %} tag that
> paralleled the {% form %} tag, except it defined your defaults for form
> rendering:
>
> {% formdefaults "table" %}
> {% widget ... %}
> {% endformdefaults %}
>
> This is much more explicit, which means that a) a random new designer
> reading your templates is more likely to notice that global defaults are
> being defined, and b) you're less likely to accidentally define global
> defaults because you omitted an enclosing block tag.
>
> Global state is serious business - it means action-at-a-distance. It
> should be obvious when it's happening, and hard to do accidentally.
Agreed, and a 'formdefaults' block seems like an elegant solution.
Yours,
Russ Magee %-)
Point taken. Yeah, renderform -> form does seem like the right thing. {%
formconfig %} doesn't quite sit right with me yet, but we can ponder
that name.
It could be a string, a BoundField instance, or a Field subclass.
> or how
>
> {% widget widgets.PasswordInput for "foo" %}
>
> would be interpreted as anything other than a string (i.e., is there a
> "constant" interpretation for the first two use cases).
No, if its a quoted string, then it's always a fieldname. That's why we
provide the "formfields" dictionary in the template context (via context
processor), so you have an easy way to pass in actual Field subclasses
and don't need any special "constant form" for that. (Same for the
"widgets" dictionary and the first argument; for that argument using a
quoted string would always be wrong).
>
> Is the intention to run make the interpretation of {% widget for %}
> argument based on a type check (i.e., isinstance(var, basestring) ->
> field named var, isinstance(var, Field) -> field var, issubclass(var,
> Field) -> fields of type var) of the context variable that is
> provided?
Yes, essentially, though I don't think isinstance checks will actually
be necessary in the implementation. I think we can do it with straight
duck-typing, just by comparing what was passed in for equality with the
appropriate things in the appropriate order, and taking the first match.
Carl
On 05/23/2011 04:30 AM, Jonathan Slenders wrote:
> 1. Like Carl said, I always prefer template tags which alter the
> context to create a scope. (I hate {% url ... as varname %})
>
> {% form "table" %}
> {% renderform my_form %}
> {% endform %}
Well, in any case, not all context-altering tags will create their own
scope; even if we do ditch {% formlayout %}, {% widget %} still alters
context without creating a new scope. However, if my {% formdefaults %}
proposal is used, we'd at least maintain that the context-altering tags
can only be used within a scoped tag, either {% formconfig %} or {%
formdefaults %} (though in the latter case, although there's a scope
within which the modifications must be made, they have global impact).
> 2. Also totally agreed with Russell that we need consistency about
> when template tag parameters need to be quoted.
> That is, quoted in case of constants, and unquoted when they need to
> be resolved.
Yes. I'm still not entirely understanding which part of the RFC made it
appear that it didn't follow this rule. It already does.
> And even if this seems to cover all the use cases, ensure
> extensibility.
Definitely.
Carl
I like the API and I'm looking forward to this. Just a small comment
on the {% widget %} tag:
On Sun, May 22, 2011 at 10:21 PM, Gregor Müllegger <gre...@muellegger.de> wrote:
> (You can read this RFC online if you prefer:
> https://github.com/gregmuellegger/gsoc2011-stuff/blob/master/rfc_syntax.rst )
> {% widget [<widget class or instance>] [using <template name>] for
> <field type> [with <varname>=<varvalue> ...] %}
>
> In this syntax description <field type> means that you can specify one of
> three things as argument:
We just had a chat about the {% widget %} tag on IRC, it would be
useful to let template authors add attributes to the widget without
having to alter the template *and* add a context variable. Something
like:
{% widget field width foo="bar" attrs placeholder="john....@example.com" %}
Instead of {% widget field with boo="bar" placehodler="..." using
"some_template.html" %} and having to copy the default template to add
the placeholder next to the attrs.
The 'with' keyword would be used to add template variables, the
'attrs' keyword to add new attributes. The content of the context and
the attrs also needs to be checked: switching the input type or the
placeholder from the template is safe, I think but people shouldn't be
able to overwrite the input name for instance. There needs to be a
clear separation between what can be touched and what can't.
For the record, in my work on template widgets, the input's "name",
"value" and "type" are passed directly in the template context, not in
the attrs dict (which contains only the id by default). We chose to be
explicit but people may expect a different behaviour :)
Cheers,
Bruno
Yes, defining a global default is really useful, we shouldn't skip that.
> For the case-by-case override, though, I'd still much rather write
>
> {% form "table" %}
> {% renderform myform %}
> {% endform %}
>
> than
>
> {% form %}
> {% formlayout "table" %}
> {% renderform myform %}
> {% endform %}
>
>
> What if instead of allowing form modifier tags to appear unenclosed, and
> making them then implicitly global, we had a {% formdefaults %} tag that
> paralleled the {% form %} tag, except it defined your defaults for form
> rendering:
>
> {% formdefaults "table" %}
> {% widget ... %}
> {% endformdefaults %}
>
> This is much more explicit, which means that a) a random new designer
> reading your templates is more likely to notice that global defaults are
> being defined, and b) you're less likely to accidentally define global
> defaults because you omitted an enclosing block tag.
A question for my own understanding: The difference between your
{% formdefaults %} variant and my "modify a global scope" is just a
syntactical difference i.e. the following examples are equal?
(using your syntax)
{% formdefaults %}
{% widget ... %}
{% endformdefaults %}
{% form %}
{% renderform my_form %}
{% endform %}
(using the RFC syntax)
{% widget ... %}
{% form %}
{% renderform my_form %}
{% endform %}
And you propably want to raise a TemplateSyntaxError if the {% widget ... %}
tag is used outside a {% formdefaults %} or {% form %} enclosed block?
I'm not sure yet, if it's worth the extra {% formdefaults %} tag. Ofcourse it
prevents template authors from excidentally overriding defaults, but I think
who will use these utilities must know about these implications anyway. It's
somehow just reflecting the behaviour of a python module scope.
But maybe I'm a bit too much programmer here and reflecting too much
of my habits
onto the template authors mind.
After all I have no objections against the {% formdefaults %} proposal and
would be happy implementing it if thats your prefered way.
2011/5/23 Preston Timmons <preston...@gmail.com>:
> This looks interesting so far.
>
> How does setting the form layout affect rendering individual fields in
> a form?
>
> Is the output of {% renderform my_form "first_name" %} equivalent to
> {{ form.first_name }}, or is it more like the output of "as_p" or
> "as_table"?
It's not really equivalent to {{ form.first_name }} because the template
variable would only render the widget. The {% renderform %} is meant to
render complete rows, including field errors, help text, the label etc.
This means you could list all the fields of a form by hand and it would
result in the same output, i.e. the following examples would result in the
same output:
(assuming my_form only has two fields)
{% rendeform my_form "first_name" %}
{% rendeform my_form "last_name" %}
-- or --
{% rendeform my_form %}
> Which templates are involved in a form layout?
This is not yet defined in detail. I would suggest a schema like:
forms/layouts/<layout name>/<template name>.html
Resulting in templates for the layout "table" like:
forms/layouts/table/row.html
forms/layouts/table/label.html
forms/layouts/table/field.html
forms/layouts/table/errors.html
forms/layouts/table/help_text.html
But this might change if we see it fit while using it in sample projects
during the summer.
A note on something different: We haven't specified yet how it will look like
to render just parts of a single field like errors. If we keep the
{{ field.errors }} syntax (which is unlikely since the template variable has
no access to the layout set by {% formlayout %}) or if we use an extra
templatetag for that. A proposal for these details will follow after we have
agreed on the higherlevel things like described in the RFC.
On 05/24/2011 09:25 AM, Gregor M�llegger wrote:
>
> Yes, defining a global default is really useful, we shouldn't skip that.
Yep, I'm definitely converted to that position ;-)
>> For the case-by-case override, though, I'd still much rather write
>>
>> {% form "table" %}
>> {% renderform myform %}
>> {% endform %}
>>
>> than
>>
>> {% form %}
>> {% formlayout "table" %}
>> {% renderform myform %}
>> {% endform %}
>>
>>
>> What if instead of allowing form modifier tags to appear unenclosed, and
>> making them then implicitly global, we had a {% formdefaults %} tag that
>> paralleled the {% form %} tag, except it defined your defaults for form
>> rendering:
>>
>> {% formdefaults "table" %}
>> {% widget ... %}
>> {% endformdefaults %}
>>
>> This is much more explicit, which means that a) a random new designer
>> reading your templates is more likely to notice that global defaults are
>> being defined, and b) you're less likely to accidentally define global
>> defaults because you omitted an enclosing block tag.
>
> A question for my own understanding: The difference between your
> {% formdefaults %} variant and my "modify a global scope" is just a
> syntactical difference i.e. the following examples are equal?
That's right.
> (using your syntax)
> {% formdefaults %}
> {% widget ... %}
> {% endformdefaults %}
>
> {% form %}
> {% renderform my_form %}
> {% endform %}
>
> (using the RFC syntax)
> {% widget ... %}
>
> {% form %}
> {% renderform my_form %}
> {% endform %}
>
> And you propably want to raise a TemplateSyntaxError if the {% widget ... %}
> tag is used outside a {% formdefaults %} or {% form %} enclosed block?
Also correct.
The other difference is that if we have {% formdefaults %} we can
specify the layout as an argument to {% formdefaults %} as well as an
argument to {% formconfig %} or whatever we name it (presuming we listen
to Russ on that naming, and I think he's right), and that means we
really wouldn't need {% formlayout %} as a separate tag anymore. Which I
think is a plus.
> I'm not sure yet, if it's worth the extra {% formdefaults %} tag. Ofcourse it
> prevents template authors from excidentally overriding defaults, but I think
> who will use these utilities must know about these implications anyway. It's
> somehow just reflecting the behaviour of a python module scope.
> But maybe I'm a bit too much programmer here and reflecting too much
> of my habits
> onto the template authors mind.
There are some key differences between this and Python module scope,
besides the usual "template authors aren't necessarily Python
programmers." The main one is that Python module scope is contained
within a single file; here we're talking about a global scope that
potentially extends across many template files, what with inheritance
and includes, and even into Python code files too. I think because of
this action-at-a-distance factor, there's a much greater need to make
the modification of global scope explicit, clear, and hard to do by
accident or overlook.
It also makes it easy to "grep" for places where the global defaults are
being set. Otherwise, there'd be no easy way to find them and separate
them out from scoped uses of the modifier tags.
Given that this is something you'd likely only do once in your project,
not over and over again, I think the downside of typing the extra tag is
minimal.
> After all I have no objections against the {% formdefaults %} proposal and
> would be happy implementing it if thats your prefered way.
I do think it's clearly better, though if you disagree of course you
should argue, not just do it my way ;-)
Carl
On 05/24/2011 09:35 AM, Gregor M�llegger wrote:
>> Which templates are involved in a form layout?
>
> This is not yet defined in detail. I would suggest a schema like:
>
> forms/layouts/<layout name>/<template name>.html
>
> Resulting in templates for the layout "table" like:
>
> forms/layouts/table/row.html
> forms/layouts/table/label.html
> forms/layouts/table/field.html
> forms/layouts/table/errors.html
> forms/layouts/table/help_text.html
This looks like a pretty good starting point to me.
Since we're building on top of Bruno's templated widgets, I think it
would also be quite useful if you could override the default widget
templates in your layout, just by placing the properly named file at the
right location. So if a widget uses the template
forms/widgets/textarea.html, and you're using "mycustom" layout, it
would first look for a template at
"forms/layouts/mycustom/widgets/textarea.html".
> A note on something different: We haven't specified yet how it will look like
> to render just parts of a single field like errors. If we keep the
> {{ field.errors }} syntax (which is unlikely since the template variable has
> no access to the layout set by {% formlayout %}) or if we use an extra
> templatetag for that. A proposal for these details will follow after we have
> agreed on the higherlevel things like described in the RFC.
Yeah. I guess it would be possible to squeeze this into the {% form %}
(nee {% renderform %}) tag with some syntactical modifiers, e.g.:
{% form "myfield:errors" %}
Or even with a filter?
{% form "myfield"|fielderrors %}
But it may be cleaner just to do it as a separate tag.
Carl
I am the author of the django-formrenderingtools package, which focuses
on a template tag library to render forms.
Currently widgets and formsets are not supported, but they could be.
I guess it matches the subject of the GSoC.
So I would be glad if you had a look at my work. It is certainly not
perfect, but I did my best.
Although the code is available since some time now (2009 as I remember)
I really miss feedback from other users. I do not know if others are
using the application, and if not, I do not know why.
I recently worked on the following items so that you can review it quickly :
* documentation
* packaging
* a demo project to discover the application and try it in a sandbox
The version 0.2.1, released on last sunday, is tagged as "beta", but I
am using it in production. I mainly miss feedback to be sure.
I am open to any suggestions if I can help:
* I can switch to Github if it helps
* I can adapt the license
* I will try to follow this GSoC and contribute to discussions
I would be glad to share my work and contribute to this topic...
Some links :
http://pypi.python.org/pypi/django-formrenderingtools/
http://packages.python.org/django-formrenderingtools/
https://bitbucket.org/benoitbryon/django-formrenderingtools/src
About the particular topic of your original post :
http://packages.python.org/django-formrenderingtools/reference/template_tags.html
http://packages.python.org/django-formrenderingtools/reference/template_names.html
--
Beno�t Bryon
Makina Corpus
In django-formrenderingtools, I first tried a schema similar to the one you are suggesting.
But I eventually chose a different schema:
forms/layouts/{{ LAYOUT_NAME }}/{{ ELEMENT_NAME }}/{{ TEMPLATE_NAME }}
where :
* {{ LAYOUT_NAME }} is "table" in your examples,
* {{ ELEMENT_NAME }} stands for row, label, field, errors...
* {{ TEMPLATE_NAME }} is "default.html by default, but can be overriden by passing a "template" argument to the template tag.
I chose this schema because it allows layout variations and thus limits the need to create new layouts.
What if one wants a specific layout for one field in one form?
* with your schema, he creates a new layout (involving several templates?).
* with the formrenderingtools schema, he overrides the layout (involving one template).
If there can be plenty of templates for several fields or forms in a layout, the layout directory could become a mess. Which ones are templates for fields? Which ones are templates for labels? We could use a filename prefix like "field_", but a folder may be more pertinent.
In addition, in formrenderingtools, field's name is used to search for a template like firstname.html before default.html.
If the template is well-named (i.e. {{ field.html_name }}.html), then there is no need to specify the new template.
I find it really useful, but it can lead to namespace errors (what if a field is named "default"?). So maybe it is not a so great idea.
For details about the template names in formrenderingtools: http://packages.python.org/django-formrenderingtools/reference/template_names.html
(it misses the "field.html_name" tip)
Layouts have to be easy to use, but also easy to create.
For the creation purpose, think about layout inheritance.
As a template designer, I do not want to rewrite or copy all the base layout when I just want to customize a few items.
As an example, Django's default behavior is to display a ":" beside the<label>.
If I want to remove or replace it, I do not want to create a brand new layout with all templates! I just want to override the<label> item.
Note: for french, we'd better use a " :". I mean the ":" element is not standard, so I don't like it and I used to remove it or sometimes use some {% trans ":" %}.
There are at least 2 ways for inheritance.
1. inheritance using {% block %} tags.
Roald de Vries showed some ways to use it in his mail on 10/06/2011.
Right now, I not convinced it is a good idea. From my point of view, the syntax is not light enough to write.
2. template fallback mecanism.
It is somehow implemented in formrenderingtools. For a given layout, you use select_template() to get a specific template and fallback to the default if necessary.
Formrenderingtools allows only 2 levels for field list elements: specific and default. As a consequence, you cannot override a layout which is not the default one. If "as_ul" layout is not the default, you cannot override just a part of it. You have to copy all the templates in a new layout and then modify a part. That is bad.
But what if we could say: "use foo layout, or fallback to, in order, bar, baz or default"?
A suggest about the n�2 implementation:
::
{% renderform %}<!-- uses default layout -->
{% form using baz %}<!-- uses baz layout or fallback to default -->
{% form using foo bar %}<!-- pushes bar then foo to the stack of layouts -->
{% endform %}
{% endform %}
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',
)
Beno�t
Can we consider that "form" is the naming convention for form context variables?
* generic views use "form"
* there is usually only one form in a view
If "form" can be considered as a convention, could we apply "convention over configuration" here?
::
{% renderform %}<!-- equivalent to {% renderform form %} -->
Convention: use the "form" variable
Configuration: pass my_form as an argument
In formrendering tools, there are 2 kinds of context:
* form context: where a "form" variable is required in the context => form, field list, form errors...
* field context: where a "form" variable is useful, and a "field" variable is required => label, field errors, field, widget...
Beno�t
However, what could be interesting with the {% formlayout %} modifier is that you could use several layouts to render one form.
As an example:
::
{% form %}
{% formlayout "p" %}
{% renderform form.field1 form.field2 %}
{% formlayout "table" %}
<table>
{% renderform form.checkbox1 form.checkbox2 form.checkbox3 %}
</table>
{% endform %}
So I would say:
* add the layout as an argument of the toplevel {% form %} tag
* and keep {% formlayout %} as an option for specific use cases
I just realized that I adviced the opposite approach for hidden fields: not in the {% form %} tag, but in modifier.
That is because I think that hiding fields is not the most common use case, whereas using a specific layout is.
Beno�t
{% renderform my_form hidden "honeypot" %}
Render all fields but outputs the my_form.honeypot field as a hidden field.
Beno�t