Learning how to CRUD data is a core skill for any web developer. CRUD stands for Create, Read, Update, Delete.
In this tutorial, I am going to the use the example of building a blogging app in Django. I am going to show you the code to create blog posts, view them, update them and delete them.
In Django, the MVC pattern (Model, View Controller) is used to structure the application code. However, Django uses different terminology.
In Django, views are the equivalent of controllers. Templates are the equivalent of views. In Django speak, MTV is the equivalent of MVC.
I know how confusing this must sound, so I’ll summarise:
Model – interacts with the database. Defined in
View – processes requests from the browser and produces responses. It pulls data from the database using the model and Django ORM. Define in
views.py. (Equivalent of a controller in the MVC pattern).
Template – renders the responses returned from the view. These are HTML files. (Equivalent of a view in MVC pattern)
CRUD is handled in
views.py, which we will focus on in this post.
In Django, you have the option to write your views as a function or a class. Django has some built-in classes that handle common tasks like logging in, CRUD and password resets. Check out Classy Class-Based Views if you’re interested in what they can do.
If you’re a beginner, I highly recommend you learn how to do CRUD using function-based views before moving on to class-based. Function-based views take more lines of code, but because you write everything yourself, it’s easier to understand what’s going on.
In this tutorial, we are only going to use function-based views.
Example: a blogging app
This post will go through how to do CRUD for a simple blogging application. You can see the full code used in this tutorial in this GitHub repository.
Before we jump into
views.py, we must first introduce
Each Django app has a file called
urls.py, which takes a URL from the user and attempts to match it to a view. In other frameworks, URLs are referred to as routes:
Our URLs map views to create, read, update, publish and delete posts.
Django takes a class-based approach to forms. Model forms can be passed to a template and rendered without the need to write any HTML. Model forms instantiated with data can be checked using the
form.is_valid() method before being saved to the database.
The gist below shows
The Create view will be called twice. Once with a GET request, which needs to return the empty form and once with a POST request to process the completed form.
We need to give an empty form to the user. In
forms.py, we created a form class, which inherits from
django.forms.ModelForm, which we have imported into our view.
add_post view, we can create an empty form by instantiating the form class:
form = PostForm().
We pass the form to the template in the context. I’ve included another key in context called
"edit_mode". This is because I’m planning to use the same template for adding and updating posts and need to add some additional logic into the templates.
render to produce a
HttpResponse for the user. It accepts three arguments: the request, the template and the context.
The template used can be found here.
When the user submits the form, a POST request is sent to the same URL, meaning our
add_view post is called again.
- Create a form
If the view receives a POST request, then the first thing it needs to do is create a form from the user’s data. The post title and body from the user’s form is stored in
request.POST. Django forms lets you instantiate a form using
request.POST like this:
- Validate the form
Forms have a method called
.isvalid() which checks the validity of the form data against the model. If the form is valid, then an object in the database can be created. If it’s not valid, then errors can be accessed through
- Create a post
If the form is valid, then we can create a post using
Not all data to create an object will be collected from a form. For example, you might want to save the post author or add a timestamp. To do this you can do:
post = form.save(commit=False) post.created = datetime.datetime.now() post.save()
The final step after creating the post is to redirect the user. Here, I have chosen to redirect them to the post detail page.
post_detail view returns data to view an individual post. It has two arguments: the request and the slug. You could use the ID instead of a slug.
Your second argument must be unique to the post, so that the correct post can be queried in the database. I’ve used django-autoslug to automatically create unique slugs for every post object.
The view needs to return either the post or a 404 error. Django has a shortcut called
get_object_or_404 for this purpose.
You can view “post_detail.html” here.
Like the create view, the update view needs to process both GET and POST requests from the user.
The GET request will instantiate the PostForm and populate it with the blog post data from the database.
The behaviour for the POST request is again similar to the create view. It needs to validate the form input and save it to the database. If the form is not valid, then errors are bound to the form object.
The template used can be found here.
The final view of this tutorial is the delete view.
I want users to confirm the deletion, the view needs to process both GET and POST requests.
The GET request will render the confirm dialog like the image below:
If the user clicks delete, then a post request will be sent back to the server to be processed by the delete view. I’ve done this by putting a submit button inside an empty form.
On receiving a POST request, the post is queried from the database using the slug provided in the URL and deletes the post.
Something I’ve learned working as a Django developer is the importance of having a soft-delete option. Instead of letting users delete objects from the UI, a boolean field called
deleted is set on the model. Views are then updated to hide “deleted” objects from the UI. This means that only users with admin access can remove (hard delete) the objects from the database.
We do this because users can make mistakes. I have often received support tickets from users wanting to reinstate deleted data.
We have covered how to write views to Create, Read, Update and Delete objects from our database, using a blogging application as an example.
We have used function based views throughout. While this involves more code than class-based views, the code is more explicit. It is important for beginners to understand the logic that goes into a function based view before introducing more abstraction with class-based views.