Hi,

2011/4/2 Russell Keith-Magee <russ...@keith-magee.com>:
> On Fri, Apr 1, 2011 at 11:57 PM, Gregor Müllegger <gre...@muellegger.de> 
> wrote:
>> I suggest reading this proposal online: https://gist.github.com/898375
>> It's exactly the same as below but formated nicely.
>>
>>
>> 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.
>
> Hi Gregor,
>
> Firstly, I'd like to echo Carl's sentiments -- this is a strong
> proposal that shows you've researched the history of Django
> discussions on this topic, and given it some thought of your own.
>
> Carl has already raised several of the points that I noticed on my
> read through. However, I do have some of my own queries.
>
> Syntax
> ~~~~~~
>
> You've proposed a syntax that goes something like:
>
> {% form myform using layout "p" and fields "firstname" "lastname" %}
> {% form fancyform.favourite_color using layout "p" %}
>
> I have two questions about this syntax.
>
> Firstly, while it looks fine for a small example, I can see how it
> would rapidly deteriorate if you have lots of fields, or lots of
> custom field requirements for each field. Django's template language
> doesn't allow you to split tags over multiple lines, so what happens
> when my tag runs over 80 characters (this simple example is already a
> 69 characters)?

(Having the possibility of multiline tags would be nice in many other cases as
well ... but that's another topic :-)

I don't see the use of more than two or three modifiers in a template tag in a
day to day use. However it's ofcourse possible to produce complex statements
that would need to span multiple lines. And it's already possible with the
syntax described in the proposal.

You can use a {% formblock %} to extract the modifiers out of the form tag:

    {% formblock using layout "uni_form" %}
    {% formblock using widget template "calendar" for myform.birthday %}
        {% form myform using fields "firstname" "lastname" "birthday" %}
    {% endformblock %}
    {% endformblock %}

Some might not like the syntax since you would encourage another level of
indention and another two lines for the closing tags. But you can use the
"configure" statement as described in the media handling section of my
proposal:

    {% form myform configure layout "uni_form" %}
    {% form myform configure widget template "calendar" for myform.birthday %}
    {% form myform using fields "firstname" "lastname" "birthday" %}

> Secondly, you appear to be using a single template tag for rendering
> both forms *and* fields. Is there a motivation behind this?

Yes there is :-) The motiviation is, that {% form myform.birthday %} is just a
shorthand for {% form myform using fields "birthday" %}. You can also use it
to iterate over a form (like in your example below) and outputting it field by
field:

    {% for field in myform %}
        {% form field %}
    {% endfor %}

I also don't see a need for an extra {% formfield %} tag. I don't see any
cases in which a {% formfield %} would be shorter, more flexible, easier to
use. It might be more readable, but I think it would end up as a stripped down
copy/alias of the form tag.

> Rendering modifiers
> ~~~~~~~~~~~~~~~~~~~~
>
> I share Carl's concern about exactly how and why these are necessary;
> even after your most recent post, I'm still not completely clear on
> what they mean in practice.
>
> On the one hand, I can see that there may be a use case for passing
> arguments into the template rendering process. However, you seem to be
> proposing a mechanism by which developers could define Python code
> that alter widget rendering by performing such tasks as:
>
>  * selecting a different rendering directory
>  * changing the template that is used for a widget
>  * changing the layout scheme
>  * prepending/appending content onto a widget
>
> I think I understand your motivation here, but my inclination is that
> this will end up being a very complex system that won't end up being
> useful in practice.

The rendering modifiers came up in the proposal because I thought about the
possible implementations of the {% form ... using ... and ... %} syntax. I had
the idea that these modifiers after "using" could be seperated from the tag
itself. I had the analogy to queryset methods in mind. E.g. the filter() method
changes the internal state of the queryset. The same would be true for
rendering modifiers, analogy: the "fields" modifiers limit the rendered
fields. These modifiers could be implemented easily as methods on the template
tag.

But with querysets you can extend the QuerySet base class and add other
convenience methods. I wanted the same for the form rendering. Why shouldn't
it be possible not only to use the rendering modifiers shipped with the form
tag but also "subclassing" it (in analogy to queryset) and extending it.

Subclassing isn't an option here because you cannot inject a new manager
into the form tag like you do with models. So I came up with the registry.

Ofcourse we don't need the registry in the first place and could skip it until
we prove that it's requested by the users of the new form rendering machinery.

> For example -- consider the case of validation. In an ideal world, it
> would be nice to be able to just throw a switch and say "turn on
> client side validation for this field", and then have that validation
> work regardless of the rendering of the field itself. However, in
> practice, it seems to me that the implementation of validation will be
> tightly bound to the rendering of the field itself -- i.e, your widget
> template would really need to be a "HTML4 text input with validation",
> not a "text widget" with the HTML4 option and the validation option
> enabled.

Yes, the validation would need to know about all the things in use, like what
does the Form.clean method do? How is the current field validated? What
special cases are used in the widget? But that would be the problem of the
clientside validation framework. I have written a very basic proposal how I
would do this in the django-rays mailing list a year ago. But let's just
declare the validation example as a from me badly chosen one. It just came to
my mind why I would like to have custom rendering modifiers.

> I can certainly see value in allowing template designers to pass in
> keyword arguments when rendering fields on a template, allowing chrome
> developers to define templates that implement various features (e.g.,
> define a TextInput template that optionally has validation). However,
> making this a complex registry and code solution seems like overkill
> to me.

I don't think it would be complex. The implementation described above would
use modifiers implemented as methods on the template tag, that are called with
the arguments given in the template. We could use a simple dict lookup instead
of a "method"-lookup:

    MODIFIERS = {
        'fields': limit_fields,
        'widget': change_widget,
        'layout': change_layout,
        ...
    }
    class FormTag(template.Node):
        ...
        def render(self, context):
            form = self.form_variable.resolve(context)
            ...
            for modifier in self.modifiers:
                modifier = MODIFIERS[modifier.name]
                form = modifier(form, modifier.arguments)
            ...

Having an API to add new keys to the MODIFIERS dict wouldn't be overkill and
much to maintain IMO. Loading new modifiers with the {% load %} tag would be
slightly more complex, I agree. But I haven't investigated on that yet what
code changes would be necessary in the template library.

But like I already wrote above: I see your concerns with adding yet another
registry that might not be used very often. I would agree on dropping that
feature if you think its better to skip this until users demand it afterwards.

> Form rows
> ~~~~~~~~~
>
> If you look at the existing as_* form layout tools, there is a
> distinction between gross form layout, and the layout of an individual
> form row.
>
> {{ form_header }}
> {% for field in form %}
>    {{ pre_field }}}
>    {{ field }}
>    {{ post_field }}
> {% endfor %}
> {{ form_footer }}
>
> Conceptually, the rendering for pre/post field could vary depending on
> the field. How is this handled in your proposal? Is it part of the
> form renderer? Or part of the widget rendering? This overlaps with
> your discussion about error templates -- since errors (and their
> rendering) is one of the major pre/post rendering activities for both
> forms and fields.

I'm not sure if I get your example right. Is it representing the output of a
"as_*" method? I looked into the django/forms/forms.py code again and I'm
quite sure that there is no header or footer of a form rendered. I also don't
see many usecases of including a header and a footer into the layout (like a
wrapping <table> or <ul> element) since this is very different from form to
form. One might want the wrapping <table> the other wants to put two forms
into one <table> element etc.

The rest of the example in between would be rendered in the "row.html"
template of the form layout. E.g. the forms/layouts/table/row.html might look
like:

    <tr><th>{{ field.label_tag }}</th>
    <td>
        {{ field.errors }}
        {{ field }}
        {{ field.help_text }}
    </td></tr>

So you could define any pre_field and post_field stuff you want. I have also
not found a case in which the current rendering makes decisions based on the
field type. I think the admin does it (e.g. for checkboxes) but also in the
template level? Maybe you can give me a more specific hint on what you mean.

Basically changing the output could be ofcourse influenced by some if tags in
the "row.html" template.

> CSS/Media
> ~~~~~~~~~
>
> I'm not wild about the idea of having to include the same form content
> twice in order to get CSS and JS included as where it is required.

What do you mean by including the same form content twice? Do you mean the
duplicate use of the form tag? I thought I had solved this via the "configure"
statement. This will record all the used options so that you don't have to
duplicate the changes made to the form.

> I can see two options here.
>
> Firstly, follow the lead of the {% cycle %} tag -- use the {% form %}
> tag to define what the form will look like, but not actually render
> where it is defined; assign the rendered properties to a context
> variable, then reference that context variable when you want to
> actually render something related to the form.
>
> {% form myform <some content> as myform_rendered %}
>
> {{ myform_rendered.media.css }}
>
> {{ myform_rendered.media.js }}
>
> {{ myform_rendered.html }}
>
> This would be fairly easy to implement, but I'm not sure it would be
> especially flexible.

I think I have described your first proposed way with the "configure"
statement in the form tag.

    {% form myform.birthday configure widget template "calendar" %}
    will use the calendar widget template for the field birthday but doesn't
    output the field, remembering the configuration for when {% form myform %}
    is used.

Rendering the form into a variable is also an alternative. But I'm not totally
happy with it and there is a reason why I didn't took this way. My idea of the
form tag included that the form rendering would be pushed down to the latest
possible point (like with the laziness of querysets). The problem with
rendering it before you output the CSS links is that you must render the form in
the head of the page to get the final CSS requirements. But that would limit
the possibilites to change the rendering later. Take this example:

    {% block extrahead %}
        {% get_comment_form for object as commentform %}
        {% form commentform as commentform_rendered %}
        {{ commentform_rendered.media.css }}
    {% endblock %}

    {% block content %}
        {% include "comments.html" %}
           ^--- I for myself have often a default template that contains
                rendering the comment form, showing latest comments etc that I
                can include on any page to show comments.
    {% endblock %}

    in comment_form.html

    ... I could only use the rendered {{ commentform_rendered }} here and
    couldn't even change the order of the field:

    {% form commentform using fields "name" "email" "comment" %}
       ^--- skipping the "url" field here ... not possible with a prerendered
            form

> Secondly, it may be possible to play a trick on the template compiler.
> Jonas Obrist suggested this trick to me at Djangocon (US) last year;
> however, I haven't had a chance to dig into it any more.
>
> The trick goes something like this:
>
> When you parse a template tag like an {% if %}, the template parser
> scans forward until it reaches an {% endif %}. All the node content
> that is discovered is then provided to the if node as the subtemplate
> for the if.
>
> However, there's no specific requirement that you need to parse until
> you reach a specific tag -- you could just scan to the end of the
> document, and treat the rest of the document as if it were the nested
> content of an if that runs to the end of the page.
>
> So -- you should be able to write a "CSS" template tag that reads to
> the end of the template, and in the process, discovers all the {% form
> %} nodes on the template. From that, you can determine all the CSS
> requirements that your HTML document has.
>
> It requires a bit of a twist in the document tree -- the entire
> document effectively becomes a child of the CSS node in the template
> -- but it should allow you to get access to form rendering
> requirements before the form is actually rendered on the page.

That sounds HUGE and pretty cool. I had something like this in mind but
skipped the idea quickly because I thought that it's not be possible.
Especially with template inheritance. But if thats really possible
(theoretically) ... I will at least try it out since it would solve the media
handling problem in a very elegant way for the user.

Thanks for mentioning it. This makes me a bit enthusastic because my initial
idea was not a complete bullshit :-)

>
> Yours,
> Russ Magee %-)

Gregor

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

Reply via email to