WTForms... html, autofocus? - python

Is it possible to have some of the new attribute only attributes used in HTML5, inside of WTForms?
Eg, say you want to create a TextField with placeholder="foo", required, and autofocus attributes. How would this be done in WTForms?
In html it would look like this: <input maxlength="256" name="q" value="" placeholder="foo" autofocus required>
Note that placeholder="foo" is easily done in WTForms. autofocus and required, because they have no value, are .. well, as far as i've seen, not supported in WTForms.
Can WTForms support this?

In WTForms 1.0, released yesterday, HTML5 compact syntax is now the default. Now you can do (in jinja):
{{ form.field(autofocus=true, required=true, placeholder="foo") }}
Note that in Jinja, the literal is true instead of True but if you were to try this in the python console you will need to use the python literal True for this to work.
In WTForms 0.6.x, which used XHTML as the default output, you could do e.g.
{{ form.field(autofocus="autofocus", required="required", placeholder="foo" }}
This is the recommended way for representing boolean attributes in XHTML, and this happens to still be 100% valid HTML5 and completely equivalent, though the HTML generated is a bit more verbose.

I'm new with WTForms but it seems to me that the solution could be improved instead of using :
{{ form.field(autofocus=true, required=true, placeholder="foo") }}
use :
{% if field.flags.required %}
{{ field(autofocus=true, required=field.flags.required, placeholder="foo") }}
{% else %}
{{ field(autofocus=true, placeholder="foo") }}
{% endif %}
WTForms seems not to handle correctly required=false for 100% HTML5 and sets in the HTML an attr required="False" instead of removing the attr. Could this be improved in next version of WTForms?

You'll probably need to create a custom widget.
Check out the docs on custom widgets.

Related

How can I use Django intcomma in integerfield

I'd like to separate numerical values in the form with a comma in my Django project.
Then I found the documentation below:
https://docs.djangoproject.com/en/2.0/ref/contrib/humanize/
I thought that I can use 'intcomma' for my purpose. So I added "django.contrib.humanize" to my INSTALLED_APPS setting. And put {% load humanize %} in a template.
When I use 'intcomma' like below, it worked.
{{ 999999|intcomma }} # 999,999
But It did not work for integer form like below:
{{ form.numbers|intcomma }} # did not work.
A code was displayed to html instead of numbers like this:
<input type="number" name="int_sample" value="100000" id="id_int_sample"
My Django version is 2.0.1.
Formatting the value of an IntegerField
You access the value of an IntegerField with the .value attribute, so:
{{ form.numbers.value|intcomma }}
Note that this is the value set to the Form when you construct it. For example through initial={..} or the value that corresponds to the instance=... you pass to the field. If you for example in th form change the value, the content will not change.
Generating an <input> with number formatting
In case we want the <input> element itself to format the number separated with comma's, we will need some HTML/JavaScript. Since Django basically only renders a webpage. How the webpage behaves in the browser is not really the responsibility of Django.
We can for example use the following approach:
<script language="JavaScript">
function commas(input){
input.value = Number(input.value).toLocaleString();
}
</script>
{{ form.numbers }}
And the Form should then make sure the commas function is invoked:
SomeForm(forms.Form):
numbers = forms.IntegerField(
widget=forms.TextInput(attrs={'onclick':'commas(self)'})
)

Flask-Bootstrap, flask-wtf, adding class to submit button

I'm using a flask-wtf submit button as follows:
{{ wtf.form_field(form.submit, button_map={'submit': 'primary'}) }}
I want to add a class to the submit button but I can not find anything about that,
I'm pretty new with this but this http://pythonhosted.org/Flask-Bootstrap/basic-usage.html#templates did not helped me a lot.
Anything to recommend?
It's super-easy, usually, so unless Flask-Bootstrap does something odd you can just tell it what you want the class to be:
{{ wtf.form_field(form.submit, class="something", button_map={'submit': 'primary'}) }}
Generally, anything you pass to the rendering function that isn't recognised will be added as a parameter to the html, so you can do things like:
{{ wtf.form_field(form.submit, cats="mew") }}
And your resulting html field would be something like <input cats="mew" id="name" name="name" type="text" value="">

How to use Bootstrap tooltips with the Python WTForms library?

I'm building a website using the (awesome) Flask framework and the WTForms plugin. I now want to use show a Bootstrap tooltip on focus of an input field (working fiddle here), but for this I have to give the input tag the data-toggle and title attribute as follows:
<input data-toggle="tooltip" title="tooltip on focus!" type="text" placeholder="Focus me!" name="thename"/>
So I simply added them to the expression:
{{ form.plotting_value(title='tooltip on focus!', data-toggle='tooltip', class='form-control') }}
The title attribute is no problem, but the data-toggle causes a
TemplateSyntaxError: invalid syntax for function call expression
Does anybody know how I can add the data-toggle to the input field using WTForms?
Python doesn't like the hyphens, you can either give it a dictionary:
{{ form.example(**{'data-toggle': 'tooltip'}) }}
or, wtform's will helpfully decode an underscore to a hyphen:
{{ form.example(data_toggle: 'tooltip') }}

How can I include a "* required field." message in all forms with required fields?

I'm using django-crispy-forms with CRISPY_TEMPLATE_PACK = 'bootstrap3'. My templates looks like this (this is an example of the sign up form, but I have a lot of forms in my templates):
{% load crispy_forms_tags %}
<h1>Sign up</h1>
<form action="/accounts/signup/" method="POST" role="form">
{% csrf_token %}
{{ form|crispy }}
<button type="submit" class="btn btn-primary">Sing up</button>
</form>
In the rendered pages, the required fields appear correctly with an * near it to indicate that they are required, but no message appears explaining what an * means.
In my case, I'm dealing with users that are sometimes not very tech-firendly, so they might not know what an * means. I would like to show a * required field. message in the top of all forms with required fields.
I could put a <p>* required field.</p> line in the beginning of each form, but I would like to know if there is a more elegant and DRY way to do it.
Thank you!
You can override the uni_form.html template with a custom one.
https://github.com/maraujop/django-crispy-forms/blob/dev/crispy_forms/templates/bootstrap3/uni_form.html
According to the django crispy forms documentation:
https://django-crispy-forms.readthedocs.org/en/latest/crispy_tag_forms.html#change-required-fields
You will need to override the field template, that is your only option, unless you use a css hack (maybe something :before and :after for the asterisk element).
Your other option is to use the HTML crispy forms element, to make a notice at the top of your form:
HTML('<strong>Fields marked with * are required</strong>')
One solution would be to either add <p class="req_legend" style="display: none;">* required field.</p> to every form and add a js which changes its display property if required fields are found.
Here is a jquery example:
$(document).ready(function() {
if ($('.requiredField').length > 0) {
$('p.req_legend').show();
}
});
Or go full javascript and add <p> element to your only if you find a requiredField, you can than do this in a javascript that you add to the Media class of your forms

How do I create a Django form that displays a checkbox label to the right of the checkbox?

When I define a Django form class similar to this:
def class MyForm(forms.Form):
check = forms.BooleanField(required=True, label="Check this")
It expands to HTML that looks like this:
<form action="." id="form" method=POST>
<p><label for="check">Check this:</label> <input type="checkbox" name="check" id="check" /></p>
<p><input type=submit value="Submit"></p>
</form>
I would like the checkbox input element to have a label that follows the checkbox, not the other way around. Is there a way to convince Django to do that?
[Edit]
Thanks for the answer from Jonas - still, while it fixes the issue I asked about (checkbox labels are rendered to the right of the checkbox) it introduces a new problem (all widget labels are rendered to the right of their widgets...)
I'd like to avoid overriding _html_output() since it's obviously not designed for it. The design I would come up with would be to implement a field html output method in the Field classes, override the one for the Boolean field and use that method in _html_output(). Sadly, the Django developers chose to go a different way, and I would like to work within the existing framework as much as possible.
CSS sounds like a decent approach, except that I don't know enough CSS to pull this off or even to decide whether I like this approach or not. Besides, I prefer markup that still resembles the final output, at least in rendering order.
Furthermore, since it can be reasonable to have more than one style sheet for any particular markup, doing this in CSS could mean having to do it multiple times for multiple styles, which pretty much makes CSS the wrong answer.
[Edit]
Seems like I'm answering my own question below. If anyone has a better idea how to do this, don't be shy.
Here's a solution I've come up with (Django v1.1):
{% load myfilters %}
[...]
{% for field in form %}
[...]
{% if field.field.widget|is_checkbox %}
{{ field }}{{ field.label_tag }}
{% else %}
{{ field.label_tag }}{{ field }}
{% endif %}
[...]
{% endfor %}
You'll need to create a custom template tag (in this example in a "myfilters.py" file) containing something like this:
from django import template
from django.forms.fields import CheckboxInput
register = template.Library()
#register.filter(name='is_checkbox')
def is_checkbox(value):
return isinstance(value, CheckboxInput)
More info on custom template tags available here.
Edit: in the spirit of asker's own answer:
Advantages:
No futzing with CSS.
The markup ends up looking the way it's supposed to.
I didn't hack Django internals. (but had to look at quite a bunch)
The template is nice, compact and idiomatic.
The filter code plays nice regardless of the exact values of the labels and input field names.
Disadvantages:
There's probably something somewhere out there that does it better and faster.
Unlikely that the client will be willing to pay for all the time spent on this just to move the label to the right...
I took the answer from romkyns and made it a little more general
def field_type(field, ftype):
try:
t = field.field.widget.__class__.__name__
return t.lower() == ftype
except:
pass
return False
This way you can check the widget type directly with a string
{% if field|field_type:'checkboxinput' %}
<label>{{ field }} {{ field.label }}</label>
{% else %}
<label> {{ field.label }} </label> {{ field }}
{% endif %}
All presented solutions involve template modifications, which are in general rather inefficient concerning performance. Here's a custom widget that does the job:
from django import forms
from django.forms.fields import BooleanField
from django.forms.util import flatatt
from django.utils.encoding import force_text
from django.utils.html import format_html
from django.utils.translation import ugettext as _
class PrettyCheckboxWidget(forms.widgets.CheckboxInput):
def render(self, name, value, attrs=None):
final_attrs = self.build_attrs(attrs, type='checkbox', name=name)
if self.check_test(value):
final_attrs['checked'] = 'checked'
if not (value is True or value is False or value is None or value == ''):
final_attrs['value'] = force_text(value)
if 'prettycheckbox-label' in final_attrs:
label = _(final_attrs.pop('prettycheckbox-label'))
else:
label = ''
return format_html('<label for="{0}"><input{1} /> {2}</label>', attrs['id'], flatatt(final_attrs), label)
class PrettyCheckboxField(BooleanField):
widget = PrettyCheckboxWidget
def __init__(self, *args, **kwargs):
if kwargs['label']:
kwargs['widget'].attrs['prettycheckbox-label'] = kwargs['label']
kwargs['label'] = ''
super(PrettyCheckboxField, self).__init__(*args, **kwargs)
# usage in form
class MyForm(forms.Form):
my_boolean = PrettyCheckboxField(label=_('Some label'), widget=PrettyCheckboxWidget())
I have PrettyCheckboxWidget and PrettyCheckboxField in an extra file, so they may be imported where needed. If you don't need translations, you can remove the ugettext parts. This code works on Django 1.5 and is untested for lower versions.
Advantages:
Highly performant, needs no template modifications
Easy to use as a custom widget
Disadvantages:
"as_table" renders the checkbox + label inside the second column
{{ field.label }} inside the template is empty. The label is instead bound to {{ field }}
More work than I planned on doing on a Saturday ;-)
I know that the user excluded CSS, but considering the top answers take about half hour of work to do such a small thing, but knowing that details like these are important on a website, I'd settle for the CSS solution.
checkbox.css
input[type="checkbox"] {
float: left;
margin-right: 10px;
margin-top: 4px;
}
forms.py
class MyForm(forms.ModelForm):
# ...
class Media:
css = {
'all': 'checkbox.css',
}
template.html
{{ form.media }}
{{ form.as_p }}
Advantages:
fast!
no futzing with template tags (just form.as_p)
no new damned widgets
the CSS file is automatically included in every form
Disadvantages:
the HTML doesn't reflect the presentation (but is good enough!)
your frontendist could complain
Here's what I ended up doing. I wrote a custom template stringfilter to switch the tags around. Now, my template code looks like this:
{% load pretty_forms %}
<form action="." method="POST">
{{ form.as_p|pretty_checkbox }}
<p><input type="submit" value="Submit"></p>
</form>
The only difference from a plain Django template is the addition of the {% load %} template tag and the pretty_checkbox filter.
Here's a functional but ugly implementation of pretty_checkbox - this code doesn't have any error handling, it assumes that the Django generated attributes are formatted in a very specific way, and it would be a bad idea to use anything like this in your code:
from django import template
from django.template.defaultfilters import stringfilter
import logging
register=template.Library()
#register.filter(name='pretty_checkbox')
#stringfilter
def pretty_checkbox(value):
# Iterate over the HTML fragment, extract <label> and <input> tags, and
# switch the order of the pairs where the input type is "checkbox".
scratch = value
output = ''
try:
while True:
ls = scratch.find('<label')
if ls > -1:
le = scratch.find('</label>')
ins = scratch.find('<input')
ine = scratch.find('/>', ins)
# Check whether we're dealing with a checkbox:
if scratch[ins:ine+2].find(' type="checkbox" ')>-1:
# Switch the tags
output += scratch[:ls]
output += scratch[ins:ine+2]
output += scratch[ls:le-1]+scratch[le:le+8]
else:
output += scratch[:ine+2]
scratch = scratch[ine+2:]
else:
output += scratch
break
except:
logging.error("pretty_checkbox caught an exception")
return output
pretty_checkbox scans its string argument, finds pairs of <label> and <input> tags, and switches them around if the <input> tag's type is "checkbox". It also strips the last character of the label, which happens to be the ':' character.
Advantages:
No futzing with CSS.
The markup ends up looking the way it's supposed to.
I didn't hack Django internals.
The template is nice, compact and idiomatic.
Disadvantages:
The filter code needs to be tested for exciting values of the labels and input field names.
There's probably something somewhere out there that does it better and faster.
More work than I planned on doing on a Saturday.
Changing checkbox position in Django admin can be quite tricky, but luckily there is a simple solution using custom widget:
from django.forms.widgets import Widget, CheckboxInput, boolean_check
class RightCheckbox(Widget):
render = CheckboxInput().render
def __init__(self, attrs=None, check_test=None):
super(RightCheckbox, self).__init__(attrs)
self.check_test = boolean_check if check_test is None else check_test
Django uses left position only when widget is subclass of CheckboxInput
Order of inputs and labels is provided via normal_row parameter of the form and there are no different row patterns for checkboxes. So there are two ways to do this (in 0.96 version exactly):
1. override _html_output of the form
2. use CSS to change position of the label and checkbox

Categories

Resources