On Sat, Jan 24, 2009 at 5:27 AM, catsclaw <ch...@subtlety.com> wrote:
>
> On Jan 22, 11:53 pm, Russell Keith-Magee <freakboy3...@gmail.com>
> wrote:
>> On Fri, Jan 23, 2009 at 5:02 AM, catsclaw <ch...@subtlety.com> wrote:

Apologies for the delay in replying - I was busy with some other items
on my Django todo list, and I knew that any reply was going to turn
into a tome, so I needed to find a solid block of time to work on it.

>> The Form (model) holds a block of form data.
>>
>> The Widget (view) describes how to render form elements.
>>
>> The Field (controller) provides a way to extract data from the user
>> interface (i.e., process raw blocks of text from a HTTP POST) and
>> convert them into data suitable for storage in the model.
>
>   But it doesn't work like that in practice.  The Form contains raw
> HTML in its implementation (in the as_p method, for example) and
> contains hardcoded links (through the BoundField class) to widgets to
> use for a TextInput or Textarea.

Yes, as a slight leakage, the Form does contain some raw HTML to
assist with the layout of complete forms. However, you can:
1) subclass and overwrite these methods to use a different HTML
2) subclass and augment these methods to provide a different rendering style
3) render an entire form without ever using the as_* family of
methods, and thus never utilize any of the HTML elements.

By Django's intended design, (3) is the preferred option - once you're
past trivial mockups, you're going to need the ability to explicitly
place widgets on a rendered page, so the as_* helpers don't actually
help that much. While they are convenient during the prototyping
phase, for the purposes of this discussion, assume they don't exist.

I would also draw your attention to the fact that Django's forms don't
render the <form> part of the page. I mention this by way of
reinforcing the fact that Django's form framework isn't about
rendering forms as complete objects - it's about providing a way to
validate the content of form data, and along the way simplify the
process of writing templates that use HTML input widgets.

> A Field contains rules for
> validating itself,

...which is exactly what a field should be doing, as part of the
controller layer.

> while the Form has rules (although not many in the
> base class) for validating groups of fields.

Yes, the form does contain exactly 1 hook for validating the entire
submitted form data, as opposed to the individual validation performed
by individual fields. If we wanted to be completely pedantic about
MVC, this form-wide validation method could be refactored into a
separate class that held all the fields and did cross-field
validation, with the form-as-Model containing a reference to this new
Controller class. However, in practical terms, this gains us nothing.
We would have two classes rather than 1, requiring more complexity for
end users, but not actually providing any additional flexibility.

>  Fields also contain HTML
> to inject into widgets.

Only for very generous interpretations of the term "HTML". I'd hardly
consider a Field indicating that it has a max_length of 100 to be
"HTML injected into a widget". The fact that it shares a name with an
HTML attribute is convenient, but not a required part of the
architecture as implemented.

>> > And it's
>> > a very limiting conception of what the form functionality ought to do
>> > for Django as well.
>>
>> I fail to see why - but this might be a matter of misunderstanding of
>> scope. Django's forms framework makes claims as a HTTP data
>> manipulation tool with some minor sideline capabilities as a HTML
>> layout tool. For any non-trivial, post-prototype application, I would
>> expect the web designer to have much more control over form layout
>> than Django's basic form renderer.
>
>    Sure, but that's the point.  If I had a trivial application, I
> wouldn't *need* a framework to help.  The web designer should have
> control over form layout.  But as it is, I can't very easily provide
> that as a programmer.  And saying "draw this field over here" through
> {{ form.field_name }} doesn't let me muddle about with the attributes
> set on that field as a web designer.

You are correct that Django's template language doesn't make it easy
to meddle with the HTML attributes of individual fields during the
rendering process -- but that's what the C in CSS is for. Django
attaches a unique ID and all the required attributes information to
widgets. Subclassed widgets will sometimes attach a class to a
rendered widget to provide something for the CSS to operate upon. The
designer shouldn't need to mess with the attributes of the individual
fields - they should be able to do all the styling they need with
appropriately cascaded CSS rules.

Of course, if you can point at a specific example of an attribute that
needs to be fiddled on a per-field basis during template rendering,
and can't be satisfied by a custom widget or CSS manipulation, please
let us know. We aren't opposed to fixing problems with Django when an
actual problem exists.

>> Coming up with perfect automated and customizable form layout has not
>> historically been a core concern of Django - simply because it is
>> impossible to well in an automated way. As a result, there hasn't been
>> much focus placed on layout of _forms_.
>>
>> If this is the feature you're looking for (and it sounds like it might
>> be) then sure - there is room for improvement. However - again - this
>> could be accommodated without a wholesale teardown of the forms
>> framework. What you're calling for is a refactor of
>> Form._html_output() to allow for easier customization of form output.
>
>   I never called for a wholesale teardown of the forms framework.
> All I asked was "Is there any work being done to clean up the way
> Django creates forms?"  In all the digging around I've done, I'd make
> the following suggestions:

Saying things like "clean up the way Django creates forms" implies
that the process is messy at present, and part of the pushback you're
getting from myself and the other core developers is that we simply
don't agree that it is messy at present.

That's not to say that there isn't room for improvement, but the
fundamentals have stood up to a long process of design interrogation
and a lot of use in the wild. The Django core developers all have a
healthy dose of pragmatism running through their veins, so abstract
criticisms about MVC purity just don't hold much water; to date you
haven't presented any problems that _clearly_ indicate a flaw in the
existing framework or proposals that will _clearly_ improve things.

However, as for your specific suggestions:

>   1) Define some "standard" attributes for fields (like "max_length",
> "max_value", "required") and document them.  Similarly, document the
> python type each field stores its value internally as, and what types
> its clean() method will accept.  Maybe this exists somewhere, but I've
> been looking directly at the code, and it's not there.

The documentation for the individual field types is here:

http://docs.djangoproject.com/en/dev/ref/forms/fields/#built-in-field-classes

However, more documentation good. Formalization of conventions good.
Happy to see any patches you care to offer in this regard.

>   2) Delete (or heavily deprecate) the Field.widget_attrs method.
> Widgets are expected to interrogate their associated Fields for this
> information.

Again, I don't see why we need to. Your proposed reorganization
doesn't provide any capabilities the existing setup lacks. The error
handling is slightly different, the data flow is push rather than
pull, but the two approaches are otherwise functionally identical.

>   3) Refactor the Form class into a Form class and a FormRenderer
> class.  The FormRenderer takes a Form and provides an HTML layout.
> The Form doesn't know any HTML, but is otherwise responsible for
> everything it currently is.

Depending on exactly what you mean here, a qualified maybe. The as_*()
family of methods uses a pretty
naive approach to form rendering - but as I said previously, that's by
design. Form._html_output() is built to satisfy the requirements of
the existing as_*() family of methods.

There may be room to clean up Form._html_output() and make it a little
more modular, and therefore easier to write your own, not quite so
naive, as_* style rendering method.

I can also see how a FormRenderer type class could be used to provide
some interesting form-wide rendering behaviour - such as an alternate
default widget set (overriding the field defaults), or a form-wide
common HTML class definition on each widget on the form. However, this
is very different to a proposal to replace as_* with a class so that
you can get the dirty dirty HTML out of the Form class. I'm also not
completely convinced that this shouldn't be handled at the template
level with a good set of template tags, given the designed control
over the widget set that is used.

>   As far as I can see, that shouldn't have especially far-reaching
> implications for the existing code.  I certainly don't have the
> experience with Django to be sure that's true.  But it does make
> significantly more sophisticated UIs available, in a straight-forward
> way, though the basic Django form mechanism.

This is the bit I have the biggest problem with. You say you're not
proposing a wholesale teardown of the forms framework, but you want to
completely reverse the widget/field ownership relationship. Oh - but
don't worry - it _shouldn't_ have any far reaching implications...
which you're not certain about, because you're not experienced with
Django.

However, let us assume for a moment that you are completely correct on
your assertion (and let's be clear - you're entire continents away
from convincing me that you are. I _am_ experienced with Django, and
what you're proposing scares the willies out of my backwards
compatibility gene).

What do we get for our efforts? Apparently, "more sophisticated UIs" -
a claim you have in no way validated. You make a passing gesture at
"client side validation", but I don't see how your arrangement makes
things any better, or couldn't be accommodated in the existing
framework.

>> Under your proposal, the widget that needed to know the maximum length
>> could interrogate the field to find out the allowed length... but
>> would need to include error handling or a default for fields that
>> don't provide a maxlength hint.
>
>   I really don't see what the widget would need to do beyond "if
> getattr(field, 'max_length', None): attr['maxLength'] =
> field.max_length".  And if the widget ignores it, all that happens is
> an overlong value gets passed to the server and rejected as an error
> on the server side.

Yes. And at the moment, all it has to do is... nothing, because the
field pushes the max_length into the attributes dictionary for the
widget. And if the widget ignores it, all that happens is an overlong
value gets passed to the server and rejected as an error on the server
side.... wait, I'm getting deja vu...

The two approaches are functionally equivalent, except for your desire
to make widgets depend on fields, rather than the other way around.
The field can't be forced to provide details it doesn't have, and the
widget can't be forced to consume properties it doesn't want.

>> However, before we (as a framework) go down the path to this sort of
>> customization, I would be interested to know what sort of form-level
>> attributes are required by every widget on a form.
>
>   "class" settings, for one.  I've got a login box that usually
> appears in the upper-right corner of the site, but sometimes appears
> in the center of the page; reusing the form but altering the class
> attributes so the one in the middle of the page is displayed in a
> larger font would be nice.  And it would be very nice to set some form-
> level validation logic (ensure either X exists or Y exists, but not
> both) and have that hooked up automatically.  That's within spitting
> distance of the changes I suggested.

In this specific use case, I again point at the C in CSS. What you're
describing about rendering a form differently depending on whether
it's in the top right or centre of screen can be completely handled
with CSS rules that cascade under a top level form.topleft and
form.centered rules.

However, I will concede that there might be a use case for having a
common class attached to every widget in a form. See my notes
previously about your FormRenderer proposal for where this might fit
in.

> PS. I didn't see any responses to my question about whether there was
> an way for a widget to know if it was being asked to render a required
> field or not.  I asked because it doesn't seem there's an easy way.
> I've got a hack to work around it--I've got hacks which work around
> *everything* I've mentioned above--but hacks are hacks; they're ugly,
> complicated, fragile, and this seemed like useful functionality for
> the main codebase to support.  That's why I asked about the form
> architecture in the first place.

I think you need to go back and read your first message in this
thread. You may have wanted to know about handling required fields,
but that isn't what you asked. You asked why "the current Django form
code is written in such a way that it makes things nearly impossible".
In the third paragraph, after describing in vague terms the
fundamental design flaws, you ask if any work is "being done to clean
up the way Django creates forms?"

That question was, you will note, answered in the very first response
you received. The subsequent 12 volume set has all been addressing the
"not great design" claim.

If the required field is your actual question: The answer is yes, it
can be done, but there isn't a simple button to press or flag to set.
django.contrib.admin does it, if you're looking for a working example.
Work your way back from AdminField in django/contrib/admin/helpers.py.

That's not the easiest way, though. The easiest approach is to write a
template filter. When you request an attribute from a form, or you
iterate over a form, you get BoundField instances. Those BoundField
instances know how to render the underlying widget, as well as the
label for the field. But they also have a handle to the field itself,
so they can discover if the field is required. A template filter can
operate on anything as a parameter - so write a template filter that
operates on the BoundField instance, interrogates for requiredness,
and outputs the label output - the label_tag() method on BoundField
already does much of the heavy lifting, so you've just got to make the
appropriate call into that functionality.

Is this something that could be made simpler? Sure. Has this exact
feature request been made before? Yes - see ticket #9079, and probably
others.

Does it require reversing the relationship between forms and widgets?
No. Does it require a fundamental rework of the forms framework? No.

Does it require someone to ask a simple question, absent of hyperbole
and drama? Absolutely.

Yours,
Russ Magee %-)

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