Posts | Tags | Archive

Add Cancel and Delete buttons to django-crispy-forms

I've been using django-crispy-forms with Bootstrap 4 on a little Django project which has saved me a ton of manual Bootstrap formatting effort. One tiny issue I came across was crispy forms has nice Submit and Button objects to add buttons to forms. However I wanted a Cancel and Delete button on my UpdateView but using the Button object will cause the form to POST which isn't what I want.

The simplest solution seems to be directly using crispy's HTML object, described aptly in the docs:

HTML: A very powerful layout object. Use it to render pure html code. In fact it behaves as a Django template and it has access to the whole context of the page where the form is being rendered:

HTML("{% if success %} <p>Operation was successful</p> {% endif %}")

Access to the whole template context? Yes please! Specifically for the delete button we need to pass a parameter, the object.id of the current object, to the delete route. Additionally the delete button is wrapped in an {% if object %} tag to only display if there is an existing form to delete. For example if you reuse the form template for your CreateView then accessing object will throw an error, it doesn't exist yet!

myapp/forms.py

class TicketForm(forms.ModelForm):
helper = FormHelper()
helper.layout = Layout(
    Fieldset(
        # ... all your layout stuff
    ),
    FormActions(
        Submit('submit', 'Save', css_class="btn btn-outline-success"),
        HTML("""<a href="{% url "ticket-list" %}" class="btn btn-secondary">Cancel</a>"""),
        HTML("""{% if object %}
                <a href="{% url "ticket-delete" object.id %}"
                class="btn btn-outline-danger pull-right">
                Delete <i class="fa fa-trash-o" aria-hidden="true"></i></button></a>
                {% endif %}"""),
    )
)

myapp/urls.py

urlpatterns = [
    url(r'^ticket/$', views.TicketListView.as_view(), name='ticket-list'),
    url(r'^ticket/new/$', views.TicketCreateView.as_view(), name='ticket-new'),
    url(r'^ticket/(?P<pk>[0-9]+)/$', views.TicketUpdateView.as_view(), name='ticket-edit'),
    url(r'^ticket/(?P<pk>[0-9]+)/delete/$', views.TicketDeleteView.as_view(), name='ticket-delete'),
]

myapp/views.py

class TicketCreateView(CreateView):
    model = Ticket
    form_class = TicketForm
    # ...

class TicketUpdateView(UpdateView):
    model = Ticket
    form_class = TicketForm
    # ...

class TicketDeleteView(DeleteView):
    model = Ticket
    form_class = TicketForm
    # ...

templates/ticket/ticket_form.html

{% extends 'page_setup.html' %}

{% load crispy_forms_tags %}

{% block content %}
  <div class="row">
    <div class="col">
      {% crispy form %}
    </div>
  </div>
{% endblock %}

© Justin Montgomery. Built using Pelican. Theme is subtle by Carey Metcalfe. Based on svbhack by Giulio Fidente.