How can I use the crispy form helper to control the layout of the form elements, for the builtin forms, such as is mentioned in THIS post?
i think its late but i hope it helps you:
forms.py
class PasswordResetFormExtra(auth.forms.PasswordResetForm):
def __init__(self, *args, **kw):
super(PasswordResetFormExtra, self).__init__(*args, **kw)
self.helper = FormHelper()
self.helper.form_class = 'form-horizontal'
self.helper.label_class = 'col-lg-2'
self.helper.field_class = 'col-lg-8'
self.helper.layout = Layout(
'email',
Div(
Submit('submit', 'Reset password', css_class='btn btn-default'),
HTML('<a class="btn btn-default" href="/">Cancel</a>'),
css_class='text-left',
)
)
urls.py
from django.contrib.auth.views import password_reset
from .forms import(
PasswordResetFormExtra,
)
urlpatterns = patterns('',
(r'^/accounts/password/reset/$', password_reset, {
'template_name': 'my_templates/password_reset.html',
'password_reset_form':PasswordResetFormExtra,
}),
)
password_reset.html
{% load crispy_forms_tags %}
{% block content %}
{% crispy form %}
{% endblock %}
Related
Good Evening,
Im having trouble with a crispy forms inlineformset. I have followed guides as per:
https://github.com/timhughes/django-cbv-inline-formset/blob/master/music/views.py
https://django-crispy-forms.readthedocs.io/en/latest/crispy_tag_formsets.html#formsets
EDIT
I think the issue is something to do with the dual submit buttons. the devicemodel form has a button that when pressed produces this error. but there is also a save button as part of the resource helper, when that's submitted I get an empty model form error.
I've added screenshots of what happens when you action each button
and I must be missing something as am getting the error:
['ManagementForm data is missing or has been tampered with']
here is my update view:
class EditDeviceModel(PermissionRequiredMixin, SuccessMessageMixin, UpdateView):
model = DeviceModel
form_class = DeviceModelForm
template_name = "app_settings/base_formset.html"
permission_required = 'config.change_devicemodel'
success_message = 'Device Type "%(model)s" saved successfully'
def get_success_url(self, **kwargs):
return '{}#device_models'.format(reverse("config:config_settings"))
def get_success_message(self, cleaned_data):
return self.success_message % dict(
cleaned_data,
model=self.object.model,
)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['title']='Edit Device Model'
if self.request.POST:
context['formset'] = DeviceFormSet(self.request.POST, instance=self.object)
else:
context['formset'] = DeviceFormSet(instance=self.object)
context['helper'] = DeviceFormSetHelper()
return context
def form_valid(self, form):
context = self.get_context_data()
formset = context['formset']
if formset.is_valid():
self.object = form.save()
formset.instance = self.object
formset.save()
return redirect(self.success_url)
else:
return self.render_to_response(self.get_context_data(form=form))
Here are my forms:
class MonitoredResourceForm(forms.ModelForm):
class Meta:
model = MonitoredResource
fields = ['resource','model']
def __init__(self, *args, **kwargs):
self.is_add = kwargs.pop("is_add", False)
super(MonitoredResourceForm, self).__init__(*args, **kwargs)
self.helper = FormHelper(self)
self.helper.form_id = 'snmp_resource_form'
self.helper.form_method = 'POST'
self.helper.layout = Layout(
Div(
Div(
Field('model'),
Field('resource', placeholder="Resource"),
css_class='col-lg-3'
),
css_class='row'
),
Div(
Div(
HTML("""<input type="submit" name="submit" value="""),
HTML('"Add' if self.is_add else '"Update' ),
HTML(""" monitored resource" class="btn btn-primary"/>"""),
HTML("""Cancel"""),
HTML("""{% if object %}
<a href="{% url 'config:delete_monitoredresource' object.id %}"
class="btn btn-danger">
Delete <i class="fa fa-trash-o" aria-hidden="true"></i></a>
{% endif %}"""),
css_class='col-lg-12'
),
css_class='row'
),
)
class DeviceModelForm(forms.ModelForm):
class Meta:
model = DeviceModel
fields = ['model','vendor','device_type','ports','uplink_speed']
def __init__(self, *args, **kwargs):
self.is_add = kwargs.pop("is_add", False)
super(DeviceModelForm, self).__init__(*args, **kwargs)
self.helper = FormHelper(self)
self.helper.form_id = 'device_type_form'
self.helper.form_method = 'POST'
self.helper.layout = Layout(
Div(
Div(
Field('model', placeholder="Model"),
Field('vendor',),
Field('device_type',),
Field('ports', placeholder="Ports"),
Field('uplink_speed', placeholder="Uplink Speed"),
css_class='col-lg-6'
),
css_class='row'
),
Div(
Div(
HTML("""<input type="submit" name="submit" value="""),
HTML('"Add' if self.is_add else '"Update' ),
HTML(""" Device Model" class="btn btn-primary"/>"""),
HTML("""Cancel"""),
HTML("""{% if object %}
<a href="{% url 'config:delete_device_model' object.id %}"
class="btn btn-danger">
Delete <i class="fa fa-trash-o" aria-hidden="true"></i></a>
{% endif %}"""),
css_class='col-lg-12'
),
css_class='row'
),
)
DeviceFormSet = inlineformset_factory(DeviceModel, MonitoredResource, form=MonitoredResourceForm, extra=1)
class DeviceFormSetHelper(FormHelper):
def __init__(self, *args, **kwargs):
super(DeviceFormSetHelper, self).__init__(*args, **kwargs)
self.form_method = 'post'
self.render_required_fields = True
self.form_id = 'snmp_resource_form'
self.form_method = 'POST'
self.add_input(Submit("submit", "Save"))
self.layout = Layout(
Div(
Div(
Field('model'),
Field('resource', placeholder="Resource"),
css_class='col-lg-6'
),
css_class='row'
),
)
and in the templates I render:
{% block content %}
{% include "home/form_errors.html" %}
<div class="col-lg-6">
{% crispy form %}
</div>
<div class="col-lg-6">
{% crispy formset helper %}
</div>
<!-- /.row -->
{% endblock %}
is anyone able to see what im missing?
I think you have to render management form in your template, explained here why you need that
Management Form is used by the formset to manage the collection of forms contained in the formset. If you don’t provide this management data, an exception will be raised
add this in view html
{{ DeviceFormSet.management_form }}
You are missing a tag and also {{format.management_form|crispy}}
I guess
Your problem is that each form in a formset has its own management_form. I haven't dealt with this specifically in crispy, but in the general formsets, that was the problem that I had. You have to manually spell out each piece of the formset, either by iteration or hardcoding, and make sure that each has its management_form.
I had the same problem and found the answer in the documentation:
{{ formset.management_form|crispy }}
{% for form in formset %}
{% crispy form %}
{% endfor %}
I try to enhance my all auth's signup form with Crispy-form but it's not working :)
I modify the allauth form in the forms.py to hide the labels and add the button
My current problem is that the "self.helper.layout = Layout" in the forms.py makes the fields to disappear in the template, but if I comment the self.helper.layout = Layout, I don't have a button to validate my form x(
I would like to have my fields and a working submit button for my form,
Thanks for your help!
details below
forms.py
from django import forms
from allauth.account.forms import SignupForm
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout, ButtonHolder, Submit,Field
from crispy_forms.bootstrap import FormActions
class MySignupForm(SignupForm):
def __init__(self, *args, **kwargs):
super(MySignupForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_show_labels = False
self.helper.layout = Layout(
FormActions(
Submit('submit', 'Sign-up', css_class = 'btn-primary')
),
)
I also define in my settings.py that I was using a custom form as signup
# CUSTOM : Allauth Setup part
AUTHENTICATION_BACKENDS = (
# Needed to login by username in Django admin, regardless of `allauth`
'django.contrib.auth.backends.ModelBackend',
# `allauth` specific authentication methods, such as login by e-mail
'allauth.account.auth_backends.AuthenticationBackend',
)
ACCOUNT_EMAIL_REQUIRED = True
ACCOUNT_AUTHENTICATED_LOGIN_REDIRECTS = False
SOCIALACCOUNT_QUERY_EMAIL = ACCOUNT_EMAIL_REQUIRED
#Since users don't have account on the landing page, sign up redirect to thank you page
LOGIN_REDIRECT_URL = '/thank-you'
ACCOUNT_FORMS = {'signup': 'public.forms.MySignupForm'}
I overrided the allauth template with this version
{% extends "base.html" %}
{% load socialaccount %}
{% load crispy_forms_tags %}
{% load i18n %}
{% block head_title %}{% trans "Signup" %}{% endblock %}
{% block content %}
<section id="contact">
<div class="row">
<div class="container">
<div class="col-lg-12 text-center">
<h1 class="section-heading">{% trans "Sign Up" %}</h1>
</div>
<form class="signup" id="signup_form" method="post" action="{% url 'thankyou' %}">
{% crispy form %}
</form>
</div>
</div>
</section>
{% endblock %}
{% crispy form %} already declares by itself <form></form>.
I think this double declaration of <form> confuses the browser.
replace:
<form class="signup" id="signup_form" method="post" action="{% url 'thankyou' %}">
{% crispy form %}
</form>
by:
{% crispy form %}
and insert into forms.py:
class MySignUpForm(SignupForm):
def __init__(self, *args, **kwargs):
super(MySignUpForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_id = 'signup_form'
self.helper.form_class = 'signup'
self.helper.form_method = 'post'
self.helper.form_action = reverse('thankyou')
# and then the rest as usual:
self.helper.form_show_labels = False
self.helper.add_input(Submit('signup', 'Sign Up'), css_class='btn btn-primary'))
This is my forms and inlineformset
class EventForm(ModelForm):
def __init__(self, *args, **kwargs):
super(EventForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_tag = False
self.helper.layout = Layout(
Field('name'),
Field('description'),
Field('tags'),
)
self.helper.layout.append(Submit('save', 'Save'))
class Meta:
model = Event
fields = ('name','description','tags', )
class GalleryForm(ModelForm):
def __init__(self, *args, **kwargs):
super(GalleryForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_tag = False
self.helper.form_class = 'form-inline'
self.helper.form.method = 'post'
self.helper.form.action = ''
self.helper.layout = Layout(
Div(
Div('title', css_class='col-md-4', ),
Div('image', css_class='col-md-4', ),
css_class='row',
),
FormActions(
Submit('submit', 'Submit'),
),
)
class Meta:
model= Gallery
fields = ('title', 'event', 'image')
GalleryFormSet = inlineformset_factory(Event, Gallery, extra=0, min_num=1, fields=('title', 'image' ))
My views:
class EventCreateView(FormsetMixin, CreateView):
template_name = 'member/event_and_gallery_form.html'
model = Event
form_class = EventForm
formset_class = GalleryFormSet
class EventUpdateView(FormsetMixin, UpdateView):
template_name = 'member/event_and_gallery_form.html'
is_update_view = True
model = Event
form_class = EventForm
formset_class = GalleryFormSet
my form.html:
{% block body %}
<form action="." method="post" enctype="multipart/form-data">
{{ formset.management_form }}
{% csrf_token %}
<legend>Event</legend>
<div class="event">
{{ form|crispy}}
</div>
<legend>
<div class="pull-right"><i class="icon-plus icon-white"></i> Add Photo</div>
Photo Gallery
</legend>
<div class="gallery form-inline">
{% for form in formset %}
{{form|crispy}}
{% endfor %}
</div>
<div class="form-actions">
<button type="submit" class="btn btn-primary">Save</button>
</div>
</form>
{% endblock %}
But it didn't give inline formset layout. How do I implement crispy form layout in my form? Help is needed.
I'm experimenting with a Django 1.4.6 project for tracking sales leads. I want this to be mobile-friendly, so I'm using Twitter Bootstrap (still on version 2.3.2) with django-crispy-forms. According to this article, the best way to do e-mail fields is <input type="email" autocapitalize="off" autocorrect="off"> and the best way to do date fields is <input type="date">. None of these attributes are implemented by default with Django and I'm wondering how best to go about implementing them. Here is the relevant code (simplified):
models.py
from django.db import models
class Lead(models.Model):
name = models.CharField(max_length=50)
email = models.EmailField(blank=True, null=True)
initial_contact_date = models.DateField()
class Meta:
ordering = ('name',)
def __unicode__(self):
return self.name
views.py
from django.core.urlresolvers import reverse
from django.views.generic import CreateView, ListView, UpdateView
from .models import Lead
class LeadAdd(CreateView):
model = Lead
def get_context_data(self, **kwargs):
context = super(LeadAdd, self).get_context_data(**kwargs)
context['title'] = 'Add a Lead'
return context
def get_success_url(self):
return reverse('lead_list')
class LeadEdit(LeadAdd, UpdateView):
def get_context_data(self, **kwargs):
context = super(LeadEdit, self).get_context_data(**kwargs)
context['title'] = 'Edit a Lead'
return context
class LeadList(ListView):
model = Lead
urls.py
from django.conf.urls import patterns, url
from .views import *
urlpatterns = patterns('',
url(r'^$', view=LeadList.as_view(), name='lead_list'),
url(r'^add/$', view=LeadAdd.as_view(), name='lead_add'),
url(r'^edit/(?P<pk>[\d]+)/$', view=LeadEdit.as_view(), name='lead_edit'),
)
lead_form.html
{% extends 'base.html' %}
{% load crispy_forms_tags %}
{% block content %}
<div class="page-header">
<h1>{{ title }}</h1>
</div>
<form action="." method="post" class="form-horizontal">
{% csrf_token %}
{{ form|crispy}}
<div class="form-actions">
<button type="submit" class="btn btn-primary">Submit</button>
</div>
</form>
{% endblock content %}
Use a field template to do this (see the docs on Field):
Field('email', template="custom-email.html")
OR create a custom widget. There is an abstract class you can use, but one of the existing predefined widgets should work:
# widgets.py
from django.forms.widgets import TextInput
class EmailInput(TextInput):
input_type = 'email'
So it might look like this in your view:
class LeadAdd(CreateView):
model = Lead
form_class = LeadAddForm
...
And then that LeadAddForm class would have your custom widget defined:
from . import widgets
LeadAddForm(forms.Form):
email = forms.CharField(
...
widget = widgets.EmailInput,
...
)
Or you can set the widget in the init:
class LeadAddForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(LeadAddForm, self).__init__(*args, **kwargs)
self.fields['email'].widget = widgets.EmailInput()
You should be able to set the extra attributes (autocapitalize="off" autocorrect="off") using the crispy form config:
Field('email', autocapitalize="off", autocorrect="off")
The simpler option:
forms.py:
class LeadForm(forms.ModelForm):
class Meta:
model = Lead
def __init__(self, *args, **kwargs):
super(LeadForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_class = 'form-horizontal'
self.helper.layout = Layout(
Fieldset('Enter the following information',
'name',
Field('email', type="email",
autocapitalize="off",
autocorrect="off"),
Field('initial_contact_date', type="date")),
FormActions(
Submit('save', 'Create Lead'),
Button('cancel', 'Cancel')
)
)
In your views.py:
class LeadAdd(CreateView):
model = Lead
success_url = reverse('lead_list')
form_class = LeadForm
def get_context_data(self, **kwargs):
context = super(LeadAdd, self).get_context_data(**kwargs)
context['title'] = 'Add a Lead'
return context
class LeadEdit(LeadAdd, UpdateView):
def get_context_data(self, **kwargs):
context = super(LeadEdit, self).get_context_data(**kwargs)
context['title'] = 'Edit a Lead'
return context
In your template:
{% extends 'base.html' %}
{% load crispy_forms_tags %}
{% block content %}
<div class="page-header">
<h1>{{ title }}</h1>
</div>
{% crispy form %}
{% endblock content %}
Hi Stackoverflow people,
I would like to style a formset with the crispy app, but it causes some grieve.
A very simple model should be presented four times.
class ItemPicture(models.Model):
item = models.ForeignKey('Item')
picture = ThumbnailerImageField(_('Image'),
upload_to='pictures/', null=True, blank=True,)
The form class is also straightforward:
class ItemPictureForm(forms.ModelForm):
class Meta:
model = ItemPicture
fields = ('picture',)
def __init__(self, *args, **kwargs):
self.helper = FormHelper()
self.helper.form_tag = False
self.helper.layout = Layout(
Fieldset(_('Add pictures'),'picture', ),
ButtonHolder(
Submit('save', _('Add'), css_class='btn btn-primary '),
Reset('reset', _('Cancel'), css_class='btn')))
super(ItemPictureForm, self).__init__(*args, **kwargs)
In my views.py, I generate the formset:
class ItemUploadPictures(FormView):
ItemPictureFormSet = formset_factory(ItemPictureForm, extra=4)
form_class = ItemPictureFormSet
template_name = 'item_upload_pictures.html'
success_url = reverse_lazy('dashboard')
My trouble is that crispy expects {% crispy formset formset.form.helper %} in the template, but it seems that the passed-through variable is form.
{% crispy form %} works, but no helper attributes will be displayed. How can I pass the entire formset information to the template?
Thank you for your suggestions.
Have you tried
{% crispy formset form.form.helper %}
forms.py
class ItemPictureForm(forms.ModelForm):
class Meta:
model = ItemPicture
fields = ('picture',)
def __init__(self, *args, **kwargs):
self.helper = FormHelper()
self.helper.form_tag = False
self.helper.layout = Layout(
Fieldset(_('Add pictures'),'picture', ),
# These need to be removed because they cant be prevented from duplicating
# ButtonHolder(
# Submit('save', _('Add'), css_class='btn btn-primary '),
# Reset('reset', _('Cancel'), css_class='btn')
))
super(ItemPictureForm, self).__init__(*args, **kwargs)
manage_pictures.html
{% load crispy_forms_tags i18n %}
<form action="" method="post">
{% csrf_token %}
{% crispy formset formset.form.helper %}
<div class="form-actions">
<input type="submit" name="save" value="{% trans "Add" %}" class="btn btn-primary" id="submit-id-save">
<input type="button" name="reset" value="{% trans "Cancel" %}" class="btn" id="button-id-cancel">
</div>
</form>
{% crispy formset formset.form.helper %}
Maybe this library serves your needs: https://github.com/runekaagaard/django-crispy-forms-fancy-formsets