Allow the User to Change Their Mind Before Deleting
The delete operation is one that we want to be particularly careful about. Once we delete an object from the database, it’s gone for good - there is no undo function. So we better be sure it’s what the user really intended to do, and it wasn’t an accident!
For this reason, CRUD interfaces will often require two steps to delete an object. For example, in the Django admin, the first step is some type of delete button (the example shown here is on the update page of the object):
Clicking this button, rather than performing the delete operation immediately, directs you to a confirmation page, giving the user a chance to change their mind:
The delete operation will only be performed against the database after clicking “Yes, I’m sure.”
Let’s implement something similar in our CRUD interface.
Add a View for the Delete Confirmation Page
We will need another view for this confirmation page, which means we also need a new URL pattern.
Let’s follow the pattern we established in the last chapter, where the update page for band 1 was placed at ‘bands/1/change/’
So the confirmation page for deleting band 1 will be: ‘bands/1/delete/’.
Go ahead and create the URL pattern, view, and basic template for our delete confirmation page. The template just needs a header so we can test that it loads correctly.
Let’s also link from the band detail page to the delete confirmation page. We already did this for the update page in the last chapter, so follow the same pattern, and refer back to that chapter if you need a reminder.
Confirm the Object to Be Deleted
When a user clicks through to the delete page, the first thing you should do is confirm which object you’re about to delete. You can do this by displaying one of the object’s fields. So let’s display the name of the band to the user, so they can double-check it’s the one they meant to delete.
First, let’s get the band object in the view and pass it to the template:
# listings/views.py
def band_delete(request, id):
band = Band.objects.get(id=id)
return render(request,
'listings/band_delete.html',
{'band': band})
Then display the band’s name in the template:
# listings/templates/listings/band_delete.html
...
<h1>Delete Band</h1>
<p>Are you sure you want to delete this band?: {{ band.name }}</p>
...
Confirm Object Deletion via a Form POST
In order to actually delete the object, we are going to use a form and an HTTP POST request. But this form is different from the ModelForm we’ve used so far - it’s not going to send any values for model fields, like name
, or genre
, because we don’t need to change any of them - we just want to delete the object as a whole.
The form is so simple that we can code it straight into the template without creating a new form class:
<h1>Delete Band</h1>
<p>Are you sure you want to delete this band?: {{ band.name }}</p>
<form action="" method="post">
{% csrf_token %}
<input type="submit" value="Delete">
</form>
The form has only one input - the submit button. (Two if you count the CSRF token.) We don’t need any more.
But how will the server know which band to delete? Shouldn’t we at least send the band id?
We are already sending the band’s id - in the URL! If we are at ‘/bands/1/change/’ and submit the form, the POST request goes back to the same URL. The id is passed to the view function, and that’s how the view knows which band to delete.
Next, in the view, we’ll use the pattern that you will recognize from the previous two chapters: we handle both the GET and the POST scenarios:
# listings/views.py
...
def band_delete(request, id):
band = Band.objects.get(id=id) # we need this for both GET and POST
if request.method == 'POST':
# delete the band from the database
band.delete()
# redirect to the bands list
return redirect('band_list'))
# no need for an `else` here. If it's a GET request, just continue
return render(request,
'listings/band_delete.html',
{'band': band})
...
Notice how there is no need for form validation here because there is no data to validate.
To delete the object, we just check if this was a POST request and then call band.delete()
.
We then redirect the user to the bands list, where they will see that the band they deleted will no longer appear in the list.
Refresh yourself on all of the steps to delete objects safely by watching the screencast.
Ok, so the user can see that the band they deleted is no longer in the list, but wouldn’t it be nicer if we showed them a confirmation message?
This is how the Django admin does it:
Flash messages are a concept found in many frameworks that allow you to display a message to a user on the next page they view, whatever that page may be (even after a redirect).
Django includes this feature in its built-in messages
app. If you’d like to use them in your web app, there are many tutorials on the subject.
Now You Try! Allow the User to Safely Delete Listing
Objects
Your tasks for this exercise are:
Add a
Listing
‘delete’ page (URL pattern, view, and template).Include a delete form in the template, and handle the GET and POST scenarios in the view.
Redirect the user to the
Listing
list after the delete operation.Link from the
Listing
detail page to the delete page.
Let’s Recap!
It’s good practice deleting objects safely by asking for user confirmation first.
You can perform the delete with
POST
to post a simple form to your delete view.
You're almost finished with this course and you'll have successfully created a Django Web application! Just one more quiz to check your skills.