Search code examples
pythondjangoformviewdjango-crispy-forms

Mixing Django crispy forms with FormView


I wanted to make a Django contact form on website and I found Django crispy forms very useful for that, but it turned out that I can't mix it with Django FormView just like this. Crispy forms have done awesome job in front-end layout, but I can't grab any information from the filled form to my Django app. I've tried this tutorial: Simple Django email form using CBV, but it's outdated and is not helpful at all. Here are my forms.py:

from django import forms
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Submit

class ContactForm(forms.Form):

    name = forms.CharField(
        label = "Name:",
        max_length = 80,
        required = True,
    )

    email = forms.CharField(
        label = "E-mail:",
        max_length = 80,
        required = True,
    )

    subject = forms.CharField(
        label = "Subject:",
        max_length = 80,
        required = True,
    )

    message = forms.CharField(
        widget = forms.Textarea,
        label = "Message:",
        required = True,
    )

    def __init__(self, *args, **kwargs):
        super(ContactForm, self).__init__(*args, **kwargs)
        self.helper = FormHelper()
        self.helper.add_input(Submit('send', 'Send'))

views.py:

from django.shortcuts import render
from myapp.forms import ContactForm

from django.core.mail import send_mail
from django.views.generic.edit import FormView

class ContactFormView(FormView):

    form_class = ContactForm
    template_name = "myapp/contact.html"
    success_url = '/email_sent/'

    def form_valid(self, form):
        message = "{name} / {email} said: ".format(
            name=form.cleaned_data.get('name'),
            email=form.cleaned_data.get('email'))
        message += "\n\n{0}".format(form.cleaned_data.get('message'))
        send_mail(
            subject=form.cleaned_data.get('subject').strip(),
            message=message,
            from_email='[email protected]',
            recipient_list=['[email protected]'],
        )
        return super(ContactFormView, self).form_valid(form)

def contact(request):
    contact_form = ContactFormView()
    return render(request, 'myapp/contact.html', {'contact_form': contact_form})

and my template contact.html:

{% extends "layout.html" %}
{% load crispy_forms_tags %}

{% block title %}Contact{% endblock %}

{% block content %}
    <h2><b>Contact</b></h2>
    <div class="container">
        <p>You can e-mail me at: <a href="mailto:[email protected]">[email protected]</a></p>
        <br>
        <p>Or simply fill the form below:</p>
        <br>
        {% crispy contact_form %}
    </div>
{% endblock %}

What I get is:

Exception Type: TypeErrorException Value:   
'ContactFormView' object is not iterable

Any suggestions how to use Django crispy forms with Django FormView?


Solution

  • This doesn't have anything to do with crispy forms. The problem is in this piece of code, where you are treating ContactFormView as if it's a form.

    def contact(request):
        contact_form = ContactFormView()
        return render(request, 'myapp/contact.html', {'contact_form': contact_form})
    

    However, ContactFormView is not a form, it's a class based view. You don't need to define another view contact, you already have a view ContactFormView.

    Change your url pattern to use the class based view:

    url(r'^contact/$', ContactFormView.as_view(), name='contact')
    

    Then in your template, include the form with:

    {{ form }}}
    

    Once the view is working with regular forms, use the crispy tag so that it is styled as you wish:

    {% crispy form %}