crispy-form's Helper doesn't take effect - django

It seems the FormHelper simply doesn't anything. Here's my Form:
class PerguntarForm(forms.Form):
title = forms.CharField(label='Título', max_length=200)
categoria = forms.ModelChoiceField(queryset=Category.objects.all(), empty_label=None)
orcamento = forms.FloatField(label='Preço máximo')
def __init__(self, *args, **kwargs):
super(PerguntarForm, self).__init__(*args, **kwargs)
self.helper = FormHelper(self)
self.helper.layout.append(Submit('save', 'save'))
self.helper.layout = Layout(
PrependedText('orcamento', ',00', active=True),
)
However, PrependedText isn't applied to the 'orcamento' field when rendered. The layout append doesn't either, which I placed just to see if something happened.
Here's the output:
<div id="div_id_title" class="form-group"><label for="id_title" class="control-label requiredField">
Título<span class="asteriskField">*</span></label><div class="controls "><input class="textinput textInput form-control" id="id_title" maxlength="200" name="title" type="text" /> </div></div><div id="div_id_categoria" class="form-group"><label for="id_categoria" class="control-label requiredField">
Categoria<span class="asteriskField">*</span></label><div class="controls "><select class="select form-control" id="id_categoria" name="categoria"><option value="4">Celular</option><option value="5">TV</option><option value="6">Computador</option></select></div></div><div id="div_id_orcamento" class="form-group"><label for="id_orcamento" class="control-label requiredField">
Preço máximo<span class="asteriskField">*</span></label><div class="controls "><input class="numberinput form-control" id="id_orcamento" name="orcamento" step="any" type="number" /> </div></div>
<input type="submit" value="Enviar" />
</form>
</div>

Make sure you are using the crispy form tag, not the crispy form filter.
{% load crispy_forms_tags %}
{% crispy form %}
Where form is the name of the form in the template.

Related

Django - how to modify crispy forms?

First, I want to generate a page that looks like this:
There are three things that makes it difficult.
1. Label + Input field set should line up horizontally
2. Some of the fields have checkbox
3. Date Field has a special Bootstrap plugin.
Here are the html source code for each types:
generic type:
<div class="col-lg-2 col-md-2 col-sm-4 col-xs-6" style="margin-bottom: 5px">
<label class="input-upper-title">Drill String Name</label>
<input type="text" id="" class="form-control input-field-height-vertical" name="" required="">
</div>
has checkbox:
<div class="col-lg-2 col-md-2 col-sm-4 col-xs-6" style="margin-bottom: 5px">
<label class="input-upper-title">String Length (ft)</label>
<div class="has-checkbox">
<input type="checkbox"><input disabled="disabled" type="text" id="" class="form-control input-field-height-vertical input-calculated" name="" data-parsley-trigger="" required="">
</div>
</div>
has date plugin:
<div class="col-lg-2 col-md-2 col-sm-4 col-xs-6" style="margin-bottom: 5px">
<label class="input-upper-title">Date Run</label>
<div class="form-group">
<div class="input-group date" id="datetimepicker7" data-target-input="nearest">
<input type="text" class="form-control datetimepicker-input input-field-height-vertical" data-target="#datetimepicker7"/>
<div class="input-group-append" data-target="#datetimepicker7" data-toggle="datetimepicker">
<div class="input-group-text"><i class="fa fa-calendar"></i></div>
</div>
</div>
</div>
</div>
And here is my forms.py:
class BHA_overall_Form(forms.ModelForm):
prefix = 'bha_overall'
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.helper = FormHelper()
class Meta():
model = BHA_overall
fields = '__all__'
How should I modify my forms.py, using crispy_forms to get the same effect as my html source code does?

Django-crispy-forms change radio button error rendering

When an error is rendered for a non-radio button, I see this:
<div id="div_id_field_a" class="control-group error">
<label for="id_field_a" class="control-label ">Field A</label>
<div class="controls">
<select class="select" id="id_field_a" name="field_a">
<option value="" selected="selected"></option>
<option value="0">0</option>
<option value="1">1</option>
</select>
<span id="error_1_id_field_a" class="help-inline"><strong>Select one</strong></span>
</div>
</div>
When an error is rendered for a radio button, I see this:
<div id="div_id_field_b" class="control-group error">
<label for="id_field_b_0" class="control-label ">Field b</label>
<div class="controls">
<p id="error_1_id_field_b" class="help-block"><strong>Enter a value</strong></p>
<label class="radio"><input type="radio" name="field_b" id="id_field_b_1" value="True">Yes</label>
<label class="radio"> <input type="radio" name="field_b" id="id_field_b_2" value="False">No</label>
</div>
</div>
How can I get the error message to appear after the radio button controls, which is what happens when a combo box used? Or at least not render the error inside a p tag and instead a span tag?
Here's how I render the form:
{% load crispy_forms_tags %}
<form id='main-form'>
{% csrf_token %}
{% crispy form form.helper %}
</form>
and the form:
class MyForm(ReoBaseForm):
field_b = forms.ChoiceField(required=False, choices=((True, 'Yes'), (False, 'No')),
label='Field b', widget=forms.RadioSelect())
def __init__(self, *args, **kwargs):
super(MyForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_tag = False

field alignments in django crispy forms and bootstrap 3?

I am using django-crispy-forms and Bootstrap 3 in my template rendering.
Here is how my form looks like:
As you can notice, the fields are not aligned correctly. I want them to be inline. please provide suggestions on how to fix this:
My crispy form code is below:
class SortFieldsForm(forms.Form):
latest_year=forms.BooleanField(label="latest year")
newest_entry=forms.BooleanField(label="newest post")
price_order=forms.ChoiceField(
widget=forms.RadioSelect,
label="price order",
choices=(('lowest_price','lowest '),('highest_price','highest'),),
initial="lowest_price",
)
def __init__(self, *args, **kwargs):
self.helper = FormHelper()
self.helper.form_class = 'form-inline'
self.helper.field_template='bootstrap3/layout/inline_field.html'
self.helper.form_method = 'post'
self.helper.form_action = '.'
super(SortFieldsForm, self).__init__(*args, **kwargs)
self.helper.layout = Layout(
InlineRadios('price_order'),
'newest_entry',
'latest_year',
Submit('submit', 'Submit', css_class='button white'),
)
And the generated HTML code:
<form action="." id="id-exampleForm" class="form-inline" method="post" >
<div id="div_id_price_order" class="form-group">
<label for="id_price_order" class="control-label requiredField">
price order
<span class="asteriskField">*</span>
</label>
<div class="controls ">
<label class="radio-inline">
<input type="radio" checked="checked" name="price_order" id="id_price_order_1" value="lowest_price" >lowest</label>
<label class="radio-inline">
<input type="radio" name="price_order" id="id_price_order_2" value="highest_price" >highest</label>
</div>
</div>
<div id="div_id_newest_entry" class="checkbox">
<label for="id_newest_entry" class=" requiredField">
<input class="checkboxinput checkbox" id="id_newest_entry" name="newest_entry" type="checkbox" />
newest post
</label>
</div>
<div id="div_id_latest_year" class="checkbox">
<label for="id_latest_year" class=" requiredField">
<input class="checkboxinput checkbox" id="id_latest_year" name="latest_year" type="checkbox" />
latest year
</label>
</div>
<input type="submit" name="submit" value="Submit" class="btn btn-primary button white" id="submit-id-submit"/>
</form>
This has nothing to do with crispy-forms itself, but rather that you have a label on the radio inputs that is pushing the actual inputs down. You need to add a margin-top style to div_id_newest_entry, submit-id-submit, and div_id_latest_year. For example (your use case may vary a bit), in your CSS file:
#div_id_newest_entry,
#div_id_latest_year,
#submit-id-submit {
margin-top: 25px;
}

Django inline formset : DELETE fied in form.visible_fields?

Maybe someone could explain this to me.
With the following models:
class ContactEmail(models.Model):
# Documentation
__doc__ = _(u'Stores an e-mail address for a contact.')
# Enums
CATEGORIES = (
(0, _(u'Personal')),
(1, _(u'Professional')),
)
# Attributes
category = models.IntegerField(choices=CATEGORIES, verbose_name=_(u'category'), help_text=_(u'This values indicates wheter the address is for personal or professional use.'))
email_address = models.EmailField(max_length=255, unique=True, verbose_name=_(u'e-mail address'), help_text=_(u'A valid e-mail address.'))
contact = models.ForeignKey('Contact', related_name=u'emails', verbose_name=_(u'contact'), help_text=_(u'The contact whose the e-mail address is.'))
priority_level = models.PositiveSmallIntegerField(verbose_name=_(u'priority level'), help_text=_(u'An integer used to define a priority level for e-mail addresses of a contact.'))
# Methodes
def __unicode__(self):
return u'%(mail)s' % {u'mail': self.email_address}
# Meta-data
class Meta:
verbose_name = _(u'E-mail')
verbose_name_plural = _(u'E-mails')
unique_together = ('contact', 'priority_level')
class Contact(models.Model):
pass
And the following ModelForms:
class ContactCreateForm(forms.ModelForm):
# Documentation
__doc__ = _(u'A custom form for Contact model.')
# Methods
def __init__(self, *args, **kwargs):
super(ContactCreateForm, self).__init__(*args, **kwargs)
for name, field in self.fields.items():
if name != 'image':
if field.widget.attrs.has_key('class'):
field.widget.attrs['class'] += ' form-control'
else:
field.widget.attrs.update({'class':'form-control'})
# Meta-data
class Meta:
model = Contact
exclude = ['second_names', 'suffix', 'dob', 'skype_account',]
class ContactEmailCreateForm(forms.ModelForm):
# Documentation
__doc__ = _(u'A custom form for ContactEmail model.')
# Methods
def __init__(self, *args, **kwargs):
super(ContactEmailCreateForm, self).__init__(*args, **kwargs)
for name, field in self.fields.items():
if field.widget.attrs.has_key('class'):
field.widget.attrs['class'] += ' form-control'
else:
field.widget.attrs.update({'class':'form-control'})
# Meta-data
class Meta:
model = ContactEmail
I'm trying to set up a create contact form that includes a formset for Emails (intention is to use django-dynamic-formset to dynamically adds form just like the Admin does - and actuall it works). Here's the view:
class ContactCreateView(LoginRequiredMixin, CreateView):
template_name = u'frontend/contacts/create.html'
model = Contact
form_class = ContactCreateForm
def get_context_data(self, **kwargs):
context = {
'emails' : inlineformset_factory(parent_model=Contact, model=ContactEmail, form=ContactEmailCreateForm, extra=1),
}
context.update(kwargs)
return super(ContactCreateView, self).get_context_data(**context)
django-dynamic-formset requires you to set can_delete=True which is set by default in inlineformset_factory. And this parameter adds a DELETE field to each form of your formset.
Until here, nothing to complain about. Except that it adds this fields to form.visible_fields which is, IMO, kind of disturbing since this field is hidden if there is no form.instance:
# create.html
<fieldset class="emails">
<legend>{% trans "E-mail(s)" %}</legend>
{{ emails.management_form }}
{% for form in emails %}
<div class="inline-form-emails">
{{ form.media }}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
{% for field in form.visible_fields %}
<div class="form-group">
<label for="{{ field.html_name }}" class="col-xs-12 col-sm-5 col-md-3 col-lg-3 control-label">
{{ field.label }} {% if field.field.required %}<span style="color: #a60000;">*</span>{% endif %}
</label>
<div class="col-xs-12 col-sm-7 col-md-9 col-lg-9">
{{ field }}
<span class="help-block">{{ field.help_text }}</span>
</div>
</div>
{% endfor %}
</div>
{% endfor %}
</fieldset>
As you can see the output:
<fieldset class="emails">
<legend>E-mail(s)</legend>
<input id="id_emails-TOTAL_FORMS" name="emails-TOTAL_FORMS" type="hidden" value="1"><input id="id_emails-INITIAL_FORMS" name="emails-INITIAL_FORMS" type="hidden" value="0"><input id="id_emails-MAX_NUM_FORMS" name="emails-MAX_NUM_FORMS" type="hidden" value="1000">
<div class="inline-form-emails dynamic-form">
<input id="id_emails-0-contact" name="emails-0-contact" type="hidden">
<input id="id_emails-0-id" name="emails-0-id" type="hidden">
<div class="form-group">
<label for="emails-0-category" class="col-xs-12 col-sm-5 col-md-3 col-lg-3 control-label">
Category <span style="color: #a60000;">*</span>
</label>
<div class="col-xs-12 col-sm-7 col-md-9 col-lg-9">
<select class="form-control" id="id_emails-0-category" name="emails-0-category">
<option value="" selected="selected">---------</option>
<option value="0">Personal</option>
<option value="1">Professional</option>
</select>
<span class="help-block">This values indicates wheter the address is for personal or professional use.</span>
</div>
</div>
<div class="form-group">
<label for="emails-0-email_address" class="col-xs-12 col-sm-5 col-md-3 col-lg-3 control-label">
E-mail address <span style="color: #a60000;">*</span>
</label>
<div class="col-xs-12 col-sm-7 col-md-9 col-lg-9">
<input class="form-control" id="id_emails-0-email_address" maxlength="255" name="emails-0-email_address" type="text">
<span class="help-block">A valid e-mail address.</span>
</div>
</div>
<div class="form-group">
<label for="emails-0-priority_level" class="col-xs-12 col-sm-5 col-md-3 col-lg-3 control-label">
Priority level <span style="color: #a60000;">*</span>
</label>
<div class="col-xs-12 col-sm-7 col-md-9 col-lg-9">
<input class="form-control" id="id_emails-0-priority_level" name="emails-0-priority_level" type="text">
<span class="help-block">An integer used to define a priority level for e-mail addresses of a contact.</span>
</div>
</div>
<div class="form-group">
<label for="emails-0-DELETE" class="col-xs-12 col-sm-5 col-md-3 col-lg-3 control-label">
Delete
</label>
<div class="col-xs-12 col-sm-7 col-md-9 col-lg-9">
<input type="hidden" name="emails-0-DELETE" id="id_emails-0-DELETE">
<span class="help-block"></span>
</div>
</div>
<a class="delete-row" href="javascript:void(0)">remove</a></div><a class="add-row" href="javascript:void(0)">add another</a>
</fieldset>
Anyone has a clue ?
As mentioned here, the problem came from the django-dynamic-form library !
Just in case someone fall into the same trap...

Django Crispy Forms, Twitter Bootstrap, and Formsets with form-inline

I'm experimenting with django-crispy-forms and Twitter Bootstrap, which is what django-crispy-forms uses as its default template pack. I'm using Django 1.4.5, django-crispy-forms 1.2.3, and Twitter Bootstrap 2.3.2.
Relevant code
urls.py
from django.conf.urls import patterns, url
from core import views
urlpatterns = patterns('',
url(r'^$', views.Survey.as_view(), name='survey'),
)
views.py
from django.views.generic.edit import FormView
from .forms import SurveyFormset
class Survey(FormView):
form_class = SurveyFormset
template_name = 'survey.html'
forms.py
from django import forms
from django.forms.formsets import formset_factory
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Submit
class SurveyForm(forms.Form):
def __init__(self, *args, **kwargs):
self.helper = FormHelper()
self.helper.form_action = '.'
self.helper.form_class = 'form-inline'
self.helper.add_input(Submit('submit', 'Submit'))
super(SurveyForm, self).__init__(*args, **kwargs)
name = forms.CharField(
widget=forms.TextInput(attrs={'placeholder': 'Name'}),
label='',
max_length=50
)
favorite_food = forms.CharField(
widget=forms.TextInput(attrs={'placeholder': 'Favorite food'}),
label='',
max_length=50
)
favorite_game = forms.CharField(
widget=forms.TextInput(attrs={'placeholder': 'Favorite game'}),
label='',
max_length=50
)
SurveyFormset = formset_factory(SurveyForm, extra=2)
survey.html
{% load crispy_forms_tags %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Survey</title>
<link href="http://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/css/bootstrap-combined.min.css" rel="stylesheet">
</head>
<body>
{% crispy form form.form.helper %}
</body>
</html>
Actual results
The form fields are all stacked even though I set the form's class to form-inline. This also makes it so that you can't tell that there are two distinct forms; it just looks like one long form with duplicate fields.
Here is the HTML being produced:
<form action="." class="form-inline" method="post">
<div style='display:none'><input type='hidden' name='csrfmiddlewaretoken' value='foo' /></div>
<div>
<input type="hidden" name="form-TOTAL_FORMS" value="2" id="id_form-TOTAL_FORMS" />
<input type="hidden" name="form-INITIAL_FORMS" value="0" id="id_form-INITIAL_FORMS" />
<input type="hidden" name="form-MAX_NUM_FORMS" value="1000" id="id_form-MAX_NUM_FORMS" />
</div>
<div id="div_id_form-0-name" class="control-group">
<div class="controls">
<input name="form-0-name" maxlength="50" placeholder="Name" type="text" class="textinput textInput" id="id_form-0-name" />
</div>
</div>
<div id="div_id_form-0-favorite_food" class="control-group">
<div class="controls">
<input name="form-0-favorite_food" maxlength="50" placeholder="Favorite food" type="text" class="textinput textInput" id="id_form-0-favorite_food" />
</div>
</div>
<div id="div_id_form-0-favorite_game" class="control-group">
<div class="controls">
<input name="form-0-favorite_game" maxlength="50" placeholder="Favorite game" type="text" class="textinput textInput" id="id_form-0-favorite_game" />
</div>
</div>
<div id="div_id_form-1-name" class="control-group">
<div class="controls">
<input name="form-1-name" maxlength="50" placeholder="Name" type="text" class="textinput textInput" id="id_form-1-name" />
</div>
</div>
<div id="div_id_form-1-favorite_food" class="control-group">
<div class="controls">
<input name="form-1-favorite_food" maxlength="50" placeholder="Favorite food" type="text" class="textinput textInput" id="id_form-1-favorite_food" />
</div>
</div>
<div id="div_id_form-1-favorite_game" class="control-group">
<div class="controls">
<input name="form-1-favorite_game" maxlength="50" placeholder="Favorite game" type="text" class="textinput textInput" id="id_form-1-favorite_game" />
</div>
</div>
<div class="form-actions">
<input type="submit" name="submit" value="Submit" class="btn btn-primary" id="submit-id-submit" />
</div>
</form>
Expected results
Since Twitter Bootstrap is what django-crispy-forms uses as its default template pack, I would expect it to handle this nicely by default. The fields should side by side, not stacked, and it should be obvious that there are two distinct forms.
I would expect the HTML to look something like this:
<form action="." class="form-inline" method="post">
<div style='display:none'><input type='hidden' name='csrfmiddlewaretoken' value='foo' /></div>
<div>
<input type="hidden" name="form-TOTAL_FORMS" value="2" id="id_form-TOTAL_FORMS" />
<input type="hidden" name="form-INITIAL_FORMS" value="0" id="id_form-INITIAL_FORMS" />
<input type="hidden" name="form-MAX_NUM_FORMS" value="1000" id="id_form-MAX_NUM_FORMS" />
</div>
<div id="id_form-0" class="control-group">
<input type="text" id="id_form-0-name" class="textinput textInput" placeholder="Name" maxlength="50" name="form-0-name">
<input type="text" id="id_form-0-favorite_food" class="textinput textInput" placeholder="Favorite food" maxlength="50" name="form-0-favorite_food">
<input type="text" id="id_form-0-favorite_game" class="textinput textInput" placeholder="Favorite game" maxlength="50" name="form-0-favorite_game">
</div>
<div id="id_form-1" class="control-group">
<input type="text" id="id_form-1-name" class="textinput textInput" placeholder="Name" maxlength="50" name="form-1-name">
<input type="text" id="id_form-1-favorite_food" class="textinput textInput" placeholder="Favorite food" maxlength="50" name="form-1-favorite_food">
<input type="text" id="id_form-1-favorite_game" class="textinput textInput" placeholder="Favorite game" maxlength="50" name="form-1-favorite_game">
</div>
<div class="form-actions">
<input type="submit" name="submit" value="Submit" class="btn btn-primary" id="submit-id-submit" />
</div>
</form>
Am I doing something wrong? What should I do to get the expected results?
I was able to replicate this issue as well. While I am not 100% certain as to the behavior, I was able to find the following in the django-crispy-forms documentation:
#forms.py
def __init__(self, *args, **kwargs):
self.helper = FormHelper()
self.helper.form_action = '.'
self.helper.form_class = 'form-inline'
self.helper.template = 'bootstrap/table_inline_formset.html'
self.helper.add_input(Submit('submit', 'Submit'))
super(SurveyForm, self).__init__(*args, **kwargs)
# views.py
from django.views.generic.edit import FormView
from .forms import SurveyFormset, SurveyForm
class Survey(FormView):
form_class = SurveyForm
template_name = 'survey.html'
def get(self, request, *args, **kwargs):
self.object = None
form_class = self.get_form_class()
form = self.get_form(form_class)
formset = SurveyFormset()
return self.render_to_response(
self.get_context_data(
form=form,
formset=formset,
)
)
def post(self, request, *args, **kwargs):
"""
Handles POST requests, instantiating a form instance and its inline
formsets with the passed POST variables and then checking them for
validity.
"""
self.object = None
form_class = self.get_form_class()
form = self.get_form(form_class)
formset = SurveyFormset(self.request.POST)
if (form.is_valid() and formset.is_valid()):
return self.form_valid(form, formset)
else:
return self.form_invalid(form, formset)
def form_valid(self, form, formset):
"""
Called if all forms are valid. Creates a Recipe instance along with
associated Ingredients and Instructions and then redirects to a
success page.
"""
self.object = form.save()
formset.instance = self.object
formset.save()
return HttpResponseRedirect(self.get_success_url())
def form_invalid(self, form, formset):
"""
Called if a form is invalid. Re-renders the context data with the
data-filled forms and errors.
"""
return self.render_to_response(
self.get_context_data(
form=form,
formset=formset,
)
)
# survey.html
{% load crispy_forms_tags %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Survey</title>
<link href="http://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/css/bootstrap-combined.min.css" rel="stylesheet">
</head>
<body>
<div class="container">
<div class="row">
<div class="span12">
{% crispy formset formset.form.helper 'bootstrap' %}
</div>
</div>
</div>
</body>
</html>
While I am curious of a solution using the divs as the django-crispy-forms documentation states it should work, I can confirm this solution works on my machine. Part of the code above is credited to Kevin Dias.