Skip to content

Instantly share code, notes, and snippets.

@gregmuellegger
Created April 1, 2011 15:46
Show Gist options
  • Select an option

  • Save gregmuellegger/898375 to your computer and use it in GitHub Desktop.

Select an option

Save gregmuellegger/898375 to your computer and use it in GitHub Desktop.

Revisions

  1. gregmuellegger revised this gist Apr 7, 2011. 1 changed file with 17 additions and 0 deletions.
    17 changes: 17 additions & 0 deletions proposal.rst
    Original file line number Diff line number Diff line change
    @@ -318,6 +318,23 @@ etc. into chrome implementations.

    Lots of work but nothing to specify here...

    Formsets
    --------

    A formset is usually a list of forms and logic to deal with them at once.
    There is no special support described yet in this proposal. But it will be
    made sure that formsets can be used just as any other forms. You will only
    need to iterate over the contained forms first::

    {% for myform in myformset.forms %}
    {% form myform %}
    {% endfor %}
    {{ myformset.management_form %}

    If a better way of dealing with formsets will come up in discussions then I'm
    happy to include this in my work for the summer. However it will be not my
    primary focus and not a goal for measuring the success of the project.

    Media aka. JS/CSS
    -----------------

  2. gregmuellegger revised this gist Apr 7, 2011. 1 changed file with 8 additions and 1 deletion.
    9 changes: 8 additions & 1 deletion proposal.rst
    Original file line number Diff line number Diff line change
    @@ -488,9 +488,16 @@ template based widgets is and much more.
    My first thought was to include template based widgets also in this proposal.
    But Bruno has already made a great effort with developing
    **django-floppyforms** that might also get merged into django. He is already
    working on a patch [3].
    working on a patch [3]. The use of template based widgets might also
    invalidate the idea of a chrome. I cannot forsee the final widget
    implementation, but this proposal might change to drop chrome in favour of
    better support of exchanging widgets on the fly and configuring them directly
    in the template.

    Links
    -----

    * [1] http://groups.google.com/group/django-developers/browse_thread/thread/cbb3aee22a0f8918
    * [2] https://gist.github.com/898375
    * [3] https://github.com/brutasse/django/compare/15667-template-widgets
    * [4] http://groups.google.com/group/django-developers/browse_thread/thread/8eb1f07bfd949ab7
  3. gregmuellegger revised this gist Apr 7, 2011. 1 changed file with 44 additions and 18 deletions.
    62 changes: 44 additions & 18 deletions proposal.rst
    Original file line number Diff line number Diff line change
    @@ -365,24 +365,50 @@ are required. An example template would look like::
    ^--- Outputting all necessary js files at the end of the document.
    {% endblock %}

    I will also check the possibility and difficulty of a new ``{% formmedia %}``
    tag that hooks into the template parsing system, reading until the end of the
    template and analyzing the use of the ``{% form %}`` tag. This way it could
    determine all changes that will be applied to the form before it gets
    rendered, including all the necessary CSS dependencies that needs to be
    imported in the header of the page.

    It is not clarified yet, if the ``{% formmedia %}`` is possible at all with
    the current template parsing implementation. There might be some risks that
    need to be sorted out before starting with the implementation:

    * By parsing from the ``{% formmedia %}`` tag until the end of the template
    might result in that all content after this tag is represented as a child
    node of it. What side effects are implied? Does it produce backwards
    incompatibilities with thirdparty template tags?

    * What happens if the ``{% form %}`` tag is changing the widget of the form
    based on a context variable?
    Alternative Syntax
    ------------------

    There is a vital discussion on the mailing list about the drafts of this
    proposal [4]. There came up some new ideas about an alternative syntax,
    avoiding the long lines that would be necessary with the proposed sytanx
    above. Here are some examples how this would look like::

    {% form myform %}
    ^--- Rendering the given form with the default layout.

    {% form myform using %}
    ^--- The form tag pushes a new layer on the context stack. Every
    configuration inbetween this block tag will only affect rendered
    forms inside of this tag.

    {% layout "uni_form" %}
    ^--- The proposed rendering modifiers are implemented as concrete
    template tags, making them {% load %}-able.

    {% field myform.firstname %}
    {% field myform.lastname %}
    ^--- Fields can be directly rendered via the field tag. The
    rendering uses the options provided earlier e.g. in the layout
    tag.

    {% endform %}

    However I want to note explicitly that all the syntaxes mentioned in this
    proposal are not stable yet and I won't pretend on using them if other
    solutions are more reasonable to use. I'm sure there will be an ongoing
    discussion on the django-developers mailing list that will yield the final
    details for this project.

    After submitting this proposal and before starting to code, I will test the
    use of multiple syntaxes in some pathological scenarios. It should provide
    evidence whether one syntax is superior. Some of these test cases might be:

    * Simply rendering a form with the default layout.
    * Changing the default layout (like the current ``{{ myform.as_ul }}``).
    * Rendering a form with ten fields but skipping the last one.
    * Rendering a form with only hidden widgets.
    * Using three forms on the same page.
    * Using a formset.

    Estimates
    ---------
  4. gregmuellegger revised this gist Apr 7, 2011. 1 changed file with 20 additions and 9 deletions.
    29 changes: 20 additions & 9 deletions proposal.rst
    Original file line number Diff line number Diff line change
    @@ -324,16 +324,27 @@ Media aka. JS/CSS
    One of the other mainpoints in the discussions I reviewed for this proposal was
    the use of JS and CSS files that must be maintained somehow to display them
    how we already do through the media framework (e.g. ``{{ form.media }}``).

    The problem with this is that with the new template tag we can change some
    of the widgets in the template and introducing new dependencies. Thats why I
    would like to have an alternative for the ``using`` argument in the
    ``{% form %}`` tag.

    If ``using`` is replaced with ``configure``, the ``{% form %}`` tag will _not_
    output the HTML in the current place. However it will record and remember the
    usage of widgets and fields to determine which media files are required. An
    example template would look like::
    of the widgets in the template, introducing new dependencies.

    The main part of my work on this problem will be some investigation if it is
    possible to create a ``{% media %}`` tag that parses the template to determine
    the changes that will be applied to the form. This way it is possible to
    include all CSS dependecies in the head of the page without sacrificing the
    possibility to change the form rendering in the template.

    After providing a proof of concept, it will be necessary to test backwards
    compatibility with other form tags and libraries.

    But there is some alternative if I find out, that the described approach is
    technically not possible or would result in an ugly hack of the template
    parsing.

    The solution will be an alternative for the ``using`` argument in the ``{%
    form %}`` tag. If ``using`` is replaced with ``configure``, the ``{% form %}``
    tag will _not_ output the HTML in the current place. However it will record
    and remember the usage of widgets and fields to determine which media files
    are required. An example template would look like::

    {% block extrahead %}
    {% form myform configure widget "CalendarInput" for myform.birthday %}
  5. gregmuellegger revised this gist Apr 7, 2011. 1 changed file with 9 additions and 7 deletions.
    16 changes: 9 additions & 7 deletions proposal.rst
    Original file line number Diff line number Diff line change
    @@ -7,7 +7,7 @@ came to django shortly before 0.96 was released and a lots of awesomeness was
    introduced with the magic removal branch.

    I'm also doing some django freelancing work since 2008 to finance my studies
    and attended DjangoCon EU in 2011. This year I would like to apply to
    and attended DjangoCon EU in 2010. This year I would like to apply to
    Google Summer of Code helping to improve Django with something that bugs me
    since quite a while: It's builtin ability to render a form straight away into
    HTML.
    @@ -78,19 +78,19 @@ want to achieve during the summer:
    eating its own dogfood. Second, to prove that the stuff I have developed is
    really a step forward and to show up big problems that occur in reallife
    scenarios, not taken into account in the blueprint.
    7. Documenting all the newly introduced templatetags, the builtin chromes,
    how to write custom chrome, how to create your own form layout etc...
    7. Documenting all the newly introduced templatetags, how to extend the
    rendering behaviour, how to create your own form layout ...

    Let's get a bit more detailed. How do I want to implement these goals?
    Let's get a bit more detailed. How will this look like?

    **1. No HTML in python source**

    I will push the formating of ``as_table``, ``as_ul`` and ``as_p`` into a
    specified template structure. A formating (e.g. ``ul``) will be called a
    layout and will live in the template directory ``forms/layouts/<layout>/...``.
    This directory will contain a single file (e.g. for the table layout)::
    This directory will contain a single file::

    forms/layouts/table/row.html
    forms/layouts/<layout name>/row.html

    Creating a new layout is as simple as putting a new directory into your
    template path and adding one file. Why putting the file into a directory? The
    @@ -103,9 +103,11 @@ Ok, but how will this look in the template where I use the form? For this we
    need a new template tag::

    {% form myform using layout "p" %}
    -- or --

    {% form myform using layout "my_custom_layout" %}

    ...

    **2. Reordering/skipping fields**

    Thats pretty straigh forward with the newly introduced template tag::
  6. gregmuellegger revised this gist Apr 4, 2011. 1 changed file with 8 additions and 6 deletions.
    14 changes: 8 additions & 6 deletions proposal.rst
    Original file line number Diff line number Diff line change
    @@ -364,11 +364,12 @@ the current template parsing implementation. There might be some risks that
    need to be sorted out before starting with the implementation:

    * By parsing from the ``{% formmedia %}`` tag until the end of the template
    might result in that all content after this tag is represented as a child node
    of it. What side effects are implied? Does it produce backwards
    incompatibilities with thirdparty template tags?
    might result in that all content after this tag is represented as a child
    node of it. What side effects are implied? Does it produce backwards
    incompatibilities with thirdparty template tags?

    * What happens if the ``{% form %}`` tag is changing the widget of the form
    based on a context variable?
    based on a context variable?

    Estimates
    ---------
    @@ -407,12 +408,13 @@ be able to emulate all the rendering that is currently possible.
    Goal: Project should be feature complete.

    9th - 11th week:

    * Validating backwards compatibility for the ``{% formmedia %}`` parsing
    implementation with thirdparty modules (see Media section).
    implementation with thirdparty modules (see Media section).
    * Converting the admin to use the new form rendering.
    * Integrating lessons learned from the admin.
    * Bugfixes and regression tests for problems that showed up in the work with
    the admin.
    the admin.

    Goal: Code should be ready to be used in sample projects.

  7. gregmuellegger revised this gist Apr 4, 2011. 1 changed file with 7 additions and 7 deletions.
    14 changes: 7 additions & 7 deletions proposal.rst
    Original file line number Diff line number Diff line change
    @@ -337,7 +337,7 @@ example template would look like::
    {% form myform configure widget "CalendarInput" for myform.birthday %}
    ^--- The new widget for the field birthday will be
    recorded, but the form will not be rendered.
    {{ form.media.css }}
    {{ myform.media.css }}
    ^--- Outputting all necessary css files.
    {% endblock %}

    @@ -348,7 +348,7 @@ example template would look like::
    {% endblock %}

    {% block footer %}
    {{ form.media.js }}
    {{ myform.media.js }}
    ^--- Outputting all necessary js files at the end of the document.
    {% endblock %}

    @@ -396,8 +396,8 @@ based on discussions from DjangoCon EU.

    Goal: All public APIs should be stable.

    6th week: Starting to write tests and implementing the {% form %} tag to be able to
    emulate all the rendering that is currently possible.
    6th week: Starting to write tests and implementing the ``{% form %}`` tag to
    be able to emulate all the rendering that is currently possible.

    7th week: Implementing the necessary rendering modifiers like "fields",
    "layout" etc. and the API for chrome.
    @@ -411,10 +411,10 @@ Goal: Project should be feature complete.
    implementation with thirdparty modules (see Media section).
    * Converting the admin to use the new form rendering.
    * Integrating lessons learned from the admin.
    * Bugfixes and regression tests for problems that occur when working with the
    admin.
    * Bugfixes and regression tests for problems that showed up in the work with
    the admin.

    Goal: Code should be ready to be used in sample projects
    Goal: Code should be ready to be used in sample projects.

    12th week: Finalizing, bugfixes and tweaking the documentation.

  8. gregmuellegger revised this gist Apr 4, 2011. 1 changed file with 26 additions and 20 deletions.
    46 changes: 26 additions & 20 deletions proposal.rst
    Original file line number Diff line number Diff line change
    @@ -376,41 +376,47 @@ Estimates
    That's it so far with the proposal. In the following I will go a bit into the
    timeline that I have in mind for the implementation.

    1st week: Examing what unittests are available for the current form rendering
    and making sure they are stable for testing backwards compatibility
    during the project.
    1st week: Starting to layout the documentation. The form tag syntax based on
    discussions from the mailing list should already be finalized.

    2nd week: Converting the current layouts into template based renderers, ensuring
    2nd week: Examing what unittests are available for the current form rendering
    and making sure they are stable for testing backwards compatibility during the
    project.

    3rd week: I will attend DjangoCon EU, hopefully giving a talk about the
    revised form rendering and collecting more feedback in an open space.

    4th week: Converting the current layouts into template based renderers, ensuring
    backwards compatibility.

    Goal: no HTML should be left now in the python source.

    3rd week: I will attend DjangoCon EU
    5th week: Mainly working on documentation and integrating the very last changes
    based on discussions from DjangoCon EU.

    4th week: Starting to write tests and implementing the {% form %} tag to be able to
    emulate all the rendering that is currently possible.
    Goal: All public APIs should be stable.

    5th week: Implementing the necessary rendering modifiers like "fields" (limiting the
    form to some selected fields) and the API for chrome.
    6th week: Starting to write tests and implementing the {% form %} tag to be able to
    emulate all the rendering that is currently possible.

    6th week: Building the registry that is necessary to register your own rendering
    modifiers and chrome.
    7th week: Implementing the necessary rendering modifiers like "fields",
    "layout" etc. and the API for chrome.

    7th week: Taking care of the media handling.
    8th week: Implementing the ``{% formmedia %}`` tag.

    Goal: Project should be feature complete.

    8th week: Converting the admin to use the new form rendering and providing hooks for
    applying chrome to some fields.

    9th week: Integrating lessons learned from the admin especially in the sense of
    making it easy to package chrome with a reusable app.
    9th - 11th week:
    * Validating backwards compatibility for the ``{% formmedia %}`` parsing
    implementation with thirdparty modules (see Media section).
    * Converting the admin to use the new form rendering.
    * Integrating lessons learned from the admin.
    * Bugfixes and regression tests for problems that occur when working with the
    admin.

    Goal: Code should be ready to be used in sample projects

    10th & 11th week: Documentation and bugfixes

    12th week: Finalizing
    12th week: Finalizing, bugfixes and tweaking the documentation.

    Unfortunatelly university is running in germany during the time that I will
    work on the project. However based on my experience with last years of
  9. gregmuellegger revised this gist Apr 4, 2011. 1 changed file with 20 additions and 12 deletions.
    32 changes: 20 additions & 12 deletions proposal.rst
    Original file line number Diff line number Diff line change
    @@ -337,7 +337,7 @@ example template would look like::
    {% form myform configure widget "CalendarInput" for myform.birthday %}
    ^--- The new widget for the field birthday will be
    recorded, but the form will not be rendered.
    {% formmedia css for myform %}
    {{ form.media.css }}
    ^--- Outputting all necessary css files.
    {% endblock %}

    @@ -347,20 +347,28 @@ example template would look like::
    "CalendarInput" widget that was specified in the other tag.
    {% endblock %}

    {% block extrajs %}
    {% formmedia js for myform %}
    {% block footer %}
    {{ form.media.js }}
    ^--- Outputting all necessary js files at the end of the document.
    {% endblock %}

    A shortform for the ``{% formmedia %}`` tag is available if one likes to load
    the css and javascript next to each other: ``{% formmedia for myform %}``

    I don't know if backwards compatibility is also necessary here. If you use the
    new ``{% form %}`` tag you will also need to update your ``{{ form.media }}``
    statements.
    However we could still provide backwards compatibility by overwriting the
    ``media`` attribute of the form instance that we have modified with the
    ``{% form ... configure .. %}`` tag.
    I will also check the possibility and difficulty of a new ``{% formmedia %}``
    tag that hooks into the template parsing system, reading until the end of the
    template and analyzing the use of the ``{% form %}`` tag. This way it could
    determine all changes that will be applied to the form before it gets
    rendered, including all the necessary CSS dependencies that needs to be
    imported in the header of the page.

    It is not clarified yet, if the ``{% formmedia %}`` is possible at all with
    the current template parsing implementation. There might be some risks that
    need to be sorted out before starting with the implementation:

    * By parsing from the ``{% formmedia %}`` tag until the end of the template
    might result in that all content after this tag is represented as a child node
    of it. What side effects are implied? Does it produce backwards
    incompatibilities with thirdparty template tags?
    * What happens if the ``{% form %}`` tag is changing the widget of the form
    based on a context variable?

    Estimates
    ---------
  10. gregmuellegger revised this gist Apr 1, 2011. 1 changed file with 22 additions and 21 deletions.
    43 changes: 22 additions & 21 deletions proposal.rst
    Original file line number Diff line number Diff line change
    @@ -369,39 +369,40 @@ That's it so far with the proposal. In the following I will go a bit into the
    timeline that I have in mind for the implementation.

    1st week: Examing what unittests are available for the current form rendering
    and making sure they are stable for testing backwards compatibility
    during the project.
    2. Converting the current layouts into template based renderers, ensuring
    backwards compatibility.
    and making sure they are stable for testing backwards compatibility
    during the project.

    2nd week: Converting the current layouts into template based renderers, ensuring
    backwards compatibility.

    Goal: no HTML should be left now in the python source.

    3. I will attend DjangoCon EU
    3rd week: I will attend DjangoCon EU

    4. Starting to write tests and implementing the {% form %} tag to be able to
    emulate all the rendering that is currently possible.
    4th week: Starting to write tests and implementing the {% form %} tag to be able to
    emulate all the rendering that is currently possible.

    5. Implementing the necessary rendering modifiers like "fields" (limiting the
    form to some selected fields) and the API for chrome.
    5th week: Implementing the necessary rendering modifiers like "fields" (limiting the
    form to some selected fields) and the API for chrome.

    6. Building the registry that is necessary to register your own rendering
    modifiers and chrome.
    6th week: Building the registry that is necessary to register your own rendering
    modifiers and chrome.

    7. Taking care of the media handling.
    7th week: Taking care of the media handling.

    Goal: Project should be feature complete.

    8. Converting the admin to use the new form rendering and providing hooks for
    applying chrome to some fields.
    8th week: Converting the admin to use the new form rendering and providing hooks for
    applying chrome to some fields.

    9. Integrating lessons learned from the admin especially in the sense of
    making it easy to package chrome with a reusable app.
    9th week: Integrating lessons learned from the admin especially in the sense of
    making it easy to package chrome with a reusable app.

    Goal: Code should be ready to be used in sample projects

    10 & 11. Documentation and bugfixes
    10th & 11th week: Documentation and bugfixes

    12. Finalizing
    12th week: Finalizing

    Unfortunatelly university is running in germany during the time that I will
    work on the project. However based on my experience with last years of
    @@ -435,6 +436,6 @@ But Bruno has already made a great effort with developing
    working on a patch [3].


    [1] http://groups.google.com/group/django-developers/browse_thread/thread/cbb3aee22a0f8918
    [2] https://gist.github.com/898375
    [3] https://github.com/brutasse/django/compare/15667-template-widgets
    * [1] http://groups.google.com/group/django-developers/browse_thread/thread/cbb3aee22a0f8918
    * [2] https://gist.github.com/898375
    * [3] https://github.com/brutasse/django/compare/15667-template-widgets
  11. gregmuellegger revised this gist Apr 1, 2011. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion proposal.rst
    Original file line number Diff line number Diff line change
    @@ -436,5 +436,5 @@ working on a patch [3].


    [1] http://groups.google.com/group/django-developers/browse_thread/thread/cbb3aee22a0f8918
    [2]
    [2] https://gist.github.com/898375
    [3] https://github.com/brutasse/django/compare/15667-template-widgets
  12. gregmuellegger created this gist Apr 1, 2011.
    440 changes: 440 additions & 0 deletions proposal.rst
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,440 @@
    GSoC 2011 Proposal - Revised form rendering
    ============================================

    Hi my name is Gregor Müllegger. I'm a Computer Science student in Germany at
    the University of Augsburg currently in the fourth year of my studies. I first
    came to django shortly before 0.96 was released and a lots of awesomeness was
    introduced with the magic removal branch.

    I'm also doing some django freelancing work since 2008 to finance my studies
    and attended DjangoCon EU in 2011. This year I would like to apply to
    Google Summer of Code helping to improve Django with something that bugs me
    since quite a while: It's builtin ability to render a form straight away into
    HTML.

    Motiviation
    -----------

    Why would I like to change the current behaviour? There are some reasons for
    this:

    1. It's hard to change the default rendering.

    It is very easy to use e.g. django's builtin rendering of a ``ul`` based
    output like ``{{ myform.as_ul }}``. But what if you want to use your own
    *layout* like ``{{ myform.as_dl }}``? You can create a new method on your
    existing forms, but that involves writing python code (out of the designers
    hand) and might not be possible for thirdparty forms.

    2. Maybe the ``as_ul`` rendering is fine for me. But maybe I want to skip that
    unnecessary field that the thirdparty form is providing. The solution would
    be to write down every single field in your template, reimplementing the
    ``ul`` layout::

    <li>{{ form.field.label_tag }}: {{ form.field }}
    {{ form.field.help_text }} {{ form.field.errors }}</li>
    {# skipping field2 here #}
    <li>{{ form.field3.label_tag }}: {{ form.field3 }}
    {{ form.field3.help_text }} {{ form.field3.errors }}</li>
    ...

    We all love DRY, so this is not acceptable.

    3. The designers I worked with are often interested on adding custom css class
    or an attribute to a form field. Most of the time this is really a pain to
    do if you don't have control over the python form code. Imagine a reusable
    app that ships with urls, views, forms. To add a single class you would
    need to: (a) overwrite the predefined url because you want (b) to specify
    an additional parameter for the view which is (c) your custom subclass of
    the thirdparty form::

    class ChangedAttributeForm(ThirdPartyForm):
    field = forms.CharField(max_length=50,
    widget=TextInput(attrs={'class': 'fancy-text-input'}))

    btw: This also violates the DRY principle since you have to redefine the
    used field type, it's attributes (like ``max_length=50``) and the widget.

    I want to tell the designer how he can do this without my help in the
    template.

    Goals I want to accomplish
    --------------------------

    After showing some of the problems that I see, are here the higher goals I
    want to achieve during the summer:

    1. All of the rendering formats should be extracted from django's python source
    into a well defined structure of customizable templates.
    2. Make it possible to reorder the use of form fields in the template without
    needing to write down the complete form.
    3. Support for *chrome* that can be added dynamically in the template to a set
    of fields, changing some aspects of the form output.
    4. Ensuring that the DRY principle is applyable to your form templates (in
    case of reordering/skipping some fields, like shown above)
    5. Ensuring total backwards compatibility for ``{{ form.as_p }}`` etc. but
    deprecating it.
    6. Converting the admin to use all of the new goodness. First, to make django
    eating its own dogfood. Second, to prove that the stuff I have developed is
    really a step forward and to show up big problems that occur in reallife
    scenarios, not taken into account in the blueprint.
    7. Documenting all the newly introduced templatetags, the builtin chromes,
    how to write custom chrome, how to create your own form layout etc...

    Let's get a bit more detailed. How do I want to implement these goals?

    **1. No HTML in python source**

    I will push the formating of ``as_table``, ``as_ul`` and ``as_p`` into a
    specified template structure. A formating (e.g. ``ul``) will be called a
    layout and will live in the template directory ``forms/layouts/<layout>/...``.
    This directory will contain a single file (e.g. for the table layout)::

    forms/layouts/table/row.html

    Creating a new layout is as simple as putting a new directory into your
    template path and adding one file. Why putting the file into a directory? The
    reason is because we can make the layout even more customizable. You are able
    in the ``row.html`` to ``{% include %}`` a ``errors.html`` and a
    ``helptext.html`` that can be overriden by the designer without completely
    rewriting the whole layout.

    Ok, but how will this look in the template where I use the form? For this we
    need a new template tag::

    {% form myform using layout "p" %}
    -- or --
    {% form myform using layout "my_custom_layout" %}

    **2. Reordering/skipping fields**

    Thats pretty straigh forward with the newly introduced template tag::

    {% form myform using fields "firstname" "lastname" "country" %}

    You can list any fields you want, skipping some of them, reordering them etc.
    Here is a more advanced example with using a layout and injecting fields of a
    second form into another one::

    {% form myform using layout "p" and fields "firstname" "lastname" %}
    {% form fancyform.favourite_color using layout "p" %}
    {% form myform using layout "p" and fields "country" %}

    What have we added to the syntax?

    a. We can use a single field instead of a form as first argument
    b. We can use multiple *rendering modifiers* after the keyword ``using``.

    *Rendering modifiers* are the bits after ``using``. They have a name (like
    ``layout`` or ``fields``) and take as many arguments as they want. You can
    seperate multiple modifiers with ``and``.

    The goal will be to make these modifiers defineable. Which means that you
    can create your own modifiers to support some of your extravagant form
    rendering needs. To support this we will need to have a rendering modifier
    *registry* or something similiar, where you can ideally load new ones with the
    existing ``{% load %}`` tag. The same will apply for custom *chrome*,
    described below.

    **3. Chrome**

    The principle of a *chrome* is a shameless plug of Russell's earlier
    suggestions on a revised form rendering [1]. Let me quickly summarize what a
    chrome might be able to do for you.

    A chrome might be useful to change the rendering of a widget. For example you
    have a simple ``<input type="text" />`` input for datetime field. But you want
    to enhance it with a calendar popup (like in django's admin). You would simply
    apply the ``calendar`` chrome to your field::

    {% form myform.birthday using calendar %}

    So what's the difference here between a chrome and a rendering modifier? The
    difference is only visible in the python level since chromes will be way
    easier to implement.

    They opperate only on fields and widgets not on a complete form. They won't
    need to hassle with argument parsing since they have a common syntax that
    will be::

    {% form <form instance> using <chrome name> [<list of arguments>] [for <filtering fields>] %}

    So a chrome is called with the ``<list of arguments>`` that are already
    evaluated, previously beeing template variables or static strings. The chrome
    is also called once for every field, which simplifies implementation again.
    The template tag syntax also provides ways of limiting the use of the chrome
    to a set of fields (but the chrome implementor doesn't need to care about
    them). It's easier to explain this with some examples::

    Will add the attribute required to _all_ fields of the form (no field filters specified):
    {% form myform using attribute "required" %}

    Calendar chrome for two specified fields:
    {% form myform using calendar for myform.birthday myform.member_since %}

    Adding the class "error" to all fields that have errors:
    {% form myform using class "error" for errors %}

    Calendar for all forms.DateTimeField fields in all of the used forms:
    {% formblock using calendar for type "DateTimeField" %}
    {% form myform %}
    {% form fancyform %}
    {% form another_form_with_lots_of_datetime_fields %}
    {% endformblock %}

    Autocomplete email addresses from your addressbook for all fields that are using the EmailInput:
    {% form myform using autocomplete "/addressbook/emails/" for widget "EmailInput" %}

    A sample chrome implementation would look like::

    def attribute(bound_field, name, value=None):
    if value is None:
    value = ''
    bound_field.field.widget.attrs[name] = value

    Look again at the example from above::

    {% form myform using attribute "placeholder" "Type in your name ..." for myform.firstname myform.lastname %}

    This template tag will call the ``attribute`` chrome on both the fields
    ``firstname`` and ``lastname`` like::

    attribute(myform['firstname'], 'placeholder', 'Type in your name ...')
    attribute(myform['lastname'], 'placeholder', 'Type in your name ...')

    There will also be the possibility of template only chromes which means that
    you don't need any python code for some simple modifications.

    Template based chromes will live in ``forms/chromes/<chrome name>.html`` and
    will get the same arguments as the proposed signature of the python function:
    ``bound_field`` and ``args``. This makes things possible like creating
    Javascript triggers after the widget::

    in the form template:

    {% form myform.client using autocomplete "/customers/" %}

    in forms/chromes/autocomplete.html:

    {{ bound_field }}
    {# ^--- will render the used widget as usual #}
    <script>
    ... javascript triggers ...
    autocomplete({ url: {{ args.0 }} });
    </script>

    **4. Keeping your form templates DRY**

    The example in **2.** is already much better than the current situation but it
    still violates the DRY principle somehow. We repeat ourselfs by listing the
    used layout three times. We can do better by grouping the modifiers with a
    *formblock*::

    {% formblock using layout "p" %}
    {% form myform using fields "firstname" "lastname" %}
    {% form fancyform.favourite_color %}
    {% form myform using fields "country" %}
    {% endformblock %}

    This will remember the modifiers and chromes that are used in the
    ``formblock`` and will apply them to all ``{% form %}`` tags that are used
    inside.

    **5. Backwards compatibility**

    Backwards compatibility is a serious thing but straight forward in the case of
    this proposal. We can fall back to use the internals of the ``{% form %}`` tag
    while rendering the form via ``{{ myform }}`` or ``{{ myform.as_ul }}``. A bit
    trickier is the use of ``{{ myform.field.label_tag }}`` and
    ``{{ myform.field.errors }}``. The proposal above doesn't include these cases.

    But this is also possible to solve. Goal 1. suggests to refactor all HTML out
    of the python source. This must include lables and errors as well. For this
    case we would create some new templates::

    forms/layouts/default/label.html
    forms/layouts/default/errors.html

    They get the bound field that is used passed in and can render there output like
    the ``label_tag`` method and the ``errors`` attribute. In the template we would
    use::

    {% form myform.birthday display errors %}
    instead of {{ myform.birthday.errors %}

    {% form myform.birthday display label %}
    instead of {{ myform.birthday.label_tag %}

    Storing these templates in the layouts directory has also some nice side
    effects. We can for example use some alternative styling of the labels and
    errors based on the current layout::

    {% formblock using layout "plain" %}
    {% form myform.birthday display label field %}
    ^--- uses the "plain" layout to render the label and the field definition
    {% form myform.birthday display errors using layout "ul" %}
    ^--- the specified "ul" layout overwrites the "p" layout from the
    formblock and displays a list of errors instead of errors
    seperated by <br> that might be used in the "p" layout.
    {% form myform.birthday display helptext %}
    {% endfor %}

    **6. Admin integration**

    The admin integration will be a real fun part for me. I already worked many
    many hours with customizing the admin. Many clients want another widget to be
    used here, adding some style changes to a field there etc.

    All the stuffs is already possible ofcourse through custom widgets but we
    still need to overwrite the forms in the admin code. A cool thing would be to
    make the use of chromes easier here.

    We will add some hooks to the ``ModelAdmin`` class that allow you to add
    chromes to fields, maybe in a way like ``formfield_overrides`` works but
    propably in a more flexible manner. This is already easy to use for most
    designers since reading the ``admin.py`` file is usually very straight forward
    and only requires some basic knowledges about assignments, lists and dicts.
    However it would be even cooler to do this in the template.

    First we will create a custom form layout for the admin. It's easy to
    overwrite as described above so that you can change the look of the field
    rows, errors, helptext pretty easily. Additionally there will be a block in
    the ``change_form.html`` template that can be overridden by the user (the
    example is showing the template ``admin/<appname>/change_form.html``)::

    {% extends "admin/change_form.html" %}

    {% block form %}
    {% form adminform using autocomplete "/customers/" for adminform.client %}
    {% endblock %}

    Besides the integration, there will be some need to convert the existing
    gimmicks like calendar popup, "add new" icon next to ForeingKey dropdowns,
    etc. into chrome implementations.

    **7. Documentation**

    Lots of work but nothing to specify here...

    Media aka. JS/CSS
    -----------------

    One of the other mainpoints in the discussions I reviewed for this proposal was
    the use of JS and CSS files that must be maintained somehow to display them
    how we already do through the media framework (e.g. ``{{ form.media }}``).

    The problem with this is that with the new template tag we can change some
    of the widgets in the template and introducing new dependencies. Thats why I
    would like to have an alternative for the ``using`` argument in the
    ``{% form %}`` tag.

    If ``using`` is replaced with ``configure``, the ``{% form %}`` tag will _not_
    output the HTML in the current place. However it will record and remember the
    usage of widgets and fields to determine which media files are required. An
    example template would look like::

    {% block extrahead %}
    {% form myform configure widget "CalendarInput" for myform.birthday %}
    ^--- The new widget for the field birthday will be
    recorded, but the form will not be rendered.
    {% formmedia css for myform %}
    ^--- Outputting all necessary css files.
    {% endblock %}

    {% block content %}
    {% form myform %}
    ^--- The form will be rendered as usual but with the
    "CalendarInput" widget that was specified in the other tag.
    {% endblock %}

    {% block extrajs %}
    {% formmedia js for myform %}
    ^--- Outputting all necessary js files at the end of the document.
    {% endblock %}

    A shortform for the ``{% formmedia %}`` tag is available if one likes to load
    the css and javascript next to each other: ``{% formmedia for myform %}``

    I don't know if backwards compatibility is also necessary here. If you use the
    new ``{% form %}`` tag you will also need to update your ``{{ form.media }}``
    statements.
    However we could still provide backwards compatibility by overwriting the
    ``media`` attribute of the form instance that we have modified with the
    ``{% form ... configure .. %}`` tag.

    Estimates
    ---------

    That's it so far with the proposal. In the following I will go a bit into the
    timeline that I have in mind for the implementation.

    1st week: Examing what unittests are available for the current form rendering
    and making sure they are stable for testing backwards compatibility
    during the project.
    2. Converting the current layouts into template based renderers, ensuring
    backwards compatibility.

    Goal: no HTML should be left now in the python source.

    3. I will attend DjangoCon EU

    4. Starting to write tests and implementing the {% form %} tag to be able to
    emulate all the rendering that is currently possible.

    5. Implementing the necessary rendering modifiers like "fields" (limiting the
    form to some selected fields) and the API for chrome.

    6. Building the registry that is necessary to register your own rendering
    modifiers and chrome.

    7. Taking care of the media handling.

    Goal: Project should be feature complete.

    8. Converting the admin to use the new form rendering and providing hooks for
    applying chrome to some fields.

    9. Integrating lessons learned from the admin especially in the sense of
    making it easy to package chrome with a reusable app.

    Goal: Code should be ready to be used in sample projects

    10 & 11. Documentation and bugfixes

    12. Finalizing

    Unfortunatelly university is running in germany during the time that I will
    work on the project. However based on my experience with last years of
    university I think that I can provide about 30 hours per week for work on the
    project, discussing questions and suggestions on the mailing list and so on.


    So thanks a lot for reading so far, I really appreciate it and would love to
    hear your feedback. Please keep in mind that the syntax is product of my own
    taste and I haven't shown it to someone else yet. I'm confident that it will
    be usable for most template authors and is also very flexible on the python
    side for customizations. But there might still be a lot of room for critiques
    and situations that I have not considered yet.

    Gregor

    (A formated version of this proposal is available at [2])


    Addendum
    --------

    Most of my inspiration came from the *Revised form rendering* thread [1]
    kicked off by Russell last year after DjangoCon EU. Thanks also goes to Jannis
    and Bruno for giving me some advice and making clear what the current state of
    template based widgets is and much more.

    My first thought was to include template based widgets also in this proposal.
    But Bruno has already made a great effort with developing
    **django-floppyforms** that might also get merged into django. He is already
    working on a patch [3].


    [1] http://groups.google.com/group/django-developers/browse_thread/thread/cbb3aee22a0f8918
    [2]
    [3] https://github.com/brutasse/django/compare/15667-template-widgets