jazzband / django-floppyforms

Full control of form rendering in the templates.
http://django-floppyforms.readthedocs.org/
Other
839 stars 145 forks source link

Rendering fails with non-empty TEMPLATE_STRING_IF_INVALID #37

Closed leon-matthews closed 12 years ago

leon-matthews commented 12 years ago

I've just started evaluating floppyforms, please forgive me if this report is bogus.

I've installed django-floppyforms and added it my project's INSTALLED_APPS. Then, in a simple contact app. replaced the import in forms.py with the floppyforms version, ie:

import floppyforms as forms
#~ from django import forms

class ContactForm(forms.ModelForm):
    class Meta:
        model = models.Message

(I haven't overridden any widgets in the Metaclass yet as when I do, per the docs, I run into an issue relating to a missing _ishidden attribute, which I'm still investigating...)

Anyway, with just those changes the form becomes a mess. We use a non-empty value of TEMPLATE_STRING_IF_INVALID during development, and now the form has two of those per input. one for the help text, one for the label. The expected behaviour is restored by either downgrading floppyforms to 0.4.7 or setting TEMPLATE_STRING_IF_INVALID to an empty string under 1.0.0.

TEMPLATE_STRING_IF_INVALID = '<INVALID>'
class ContactForm(forms.ModelForm):
    class Meta:
        model = models.Message
<form action="" method="post">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Send Now" />
</form>

django-floppyforms 1.0.0

...
<p>
    <label for="id_subject">&lt;INVALID&gt;:</label>
    <input id="id_subject" type="text" name="subject" maxlength="255" />
    <span class="helptext">&lt;INVALID&gt;</span> 
</p>
...

django-floppyforms 0.4.7

(Newline added to avoid wrapping)

<p><label for="id_subject">Subject:</label> 
<input id="id_subject" type="text" name="subject" maxlength="255" /></p>

Plain Django form

(Newline added to avoid wrapping)

<p><label for="id_subject">Subject:</label> 
<input id="id_subject" type="text" name="subject" maxlength="255" /></p>
gregmuellegger commented 12 years ago

The reason seems to be a bug (or at least an unexpected behaviour on my side) in django. It is handling invalid values differently when they are assigend to a new value via "with", which makes them truthy if TEMPLATE_STRING_IF_INVALID is set:

In [16]: Template('{% with not_exist as value %}{% if value %}Hello {{ value }}{% endif %}{% endwith %}').render(Context())
Out[16]: u'Hello &lt;INVALID&gt;'

You can fix this in your project with a quick workaround. Put this in your settings.py:

class Invalid(unicode):
    def __nonzero__(self):
        return False

TEMPLATE_STRING_IF_INVALID = Invalid('<INVALID>')

However this will still break the textarea widget with floppyforms (as a quick test showed for me). But this should have been the same behaviour in older floppyforms versions. To fix this, create in your project a floppyforms/textarea.html file with the following content:

<textarea name="{{ name }}"{% if required %} required{% endif %}{% include "floppyforms/attrs.html" %}>{{ value|default:"" }}</textarea>
gregmuellegger commented 12 years ago

Django's behaviour is even documented here: https://docs.djangoproject.com/en/dev/ref/templates/api/#invalid-template-variables

We provide documentation on that in floppyforms and the fix for the textarea.html template shortly.

leon-matthews commented 12 years ago

Thanks very much Gregor. Your help is very much appreciated!

I also found the behaviour of the 'with' tag in your explanation surprising -- although the question of what the 'with' tag should do in light of missing values is a difficult one when exceptions are verboten...

I had read the advice in the Django Docs about not using a value for TEMPLATE_STRING_IF_INVALID as a development default, but had understood it just as a backwards compatibility issue -- recent versions of the admin templates no longer require an empty value to work.