ToDo App in Django Part 5: Create, Retrieve, Update and Delete Task - CRUD Operation in Django

Hello Internet people. Welcome to Learn Django by Doing Project series. Hope you enjoying it.

In the previous tutorial, we saw how we perform CRUD operation in python shell with our Task Model. We played with ORM and Querysets stuff. Now We apply the same logic in our view and perform CRUD from the web. Let’s do it. Make sure you already activated your virtual environment.

Creating/Adding Task

To store our task into the database we need a form, so we can write our task and submit it. For that, we use Django forms.

Django has a built-in forms framework that allows you to create forms in an easy manner. The forms framework makes it simple to define the fields of your form, specify how they have to be displayed and indicate how they have to validate input data. The Django forms framework offers a flexible way to render forms and handle data.

  • Form: Allows you to build standard forms
  • ModelForm: Allows you to build forms tied to model instances

We use ModelForm here to create form. First, create a forms.py file inside the directory of your todolist app and make it looks like this:

from django import forms
from django.forms import ModelForm
from .models import Task


class TaskForm(forms.ModelForm):
    class Meta:
        model = Task
        fields = "__all__"  # include all fields in form
        # fields=('title','completed') # include particular fileds of model in form

To create a form from a model, you just need to indicate which model to use to build the form in the Meta class of the form. Django introspects the model and builds the form dynamically for you.

Now we handling this TaskForm in our view. For that, open views.py of todolist app and add the following lines.

from django.shortcuts import render, HttpResponse
from .forms import TaskForm

# Create your views here.


def index(request):
    # return HttpResponse("Hello World!!")
    form = TaskForm()
    return render(request, "index.html", {"task_form": form})

Here we build form instance with task_form = TaskForm() and we passing our form in our template index.html with context {"task_form":form}. In our template, we can access this variable with task_form key.

Now open index.html and add following lines:

{% extends 'base.html' %}

{% block title %}
Tasks - 
{% endblock title %}

{% block content %}
    <!-- <h1>Hello, world!</h1> -->

    <form method="post">
    {% csrf_token %}

        <!-- {{task_form}} to display all -->
        {{task_form.title}} <!-- to display particular field-->

        <button type="submit">SUBMIT</button>
    </form>
{% endblock content %}

We using post method in form. The {% csrf_token %} template tag introduces a hidden field with an autogenerated token to avoid cross-site request forgery (CSRF) attacks. These attacks consist of a malicious website or program performing an unwanted action for a user on your site.

By default, Django checks for the CSRF token in all POST requests. Remember to include the csrf_token tag in all forms that are submitted via POST.

Start development server and open http://127.0.0.1:8000/ in browser. You can see your form is rendered.

form

Now again go to views.py and we save our task in our database when the user enters a task in the input box and submits it. So, for that make a change in the index function of views.py like this:

def index(request):
    # return HttpResponse("Hello World!!")
    form = TaskForm()

    if request.method == "POST":
        #Get the posted form
        form = TaskForm(request.POST)
        if form.is_valid():
            form.save()
            return redirect("index")
    return render(request, "index.html", {"task_form": form})

When we submit the form it sends us back to our view. So we need to handle it; like the above code.

There are two kinds of HTTP requests, GET and POST. In Django, the request object passed as parameter to your view has an attribute called "method" where the type of the request is set, and all data passed via POST can be accessed via the request.POST dictionary.

Then you instantiate the form using the submitted data and validate it using the is_valid() method. If the form is invalid, you render the template with the validation errors. If the form is valid, a new task will store in the database by calling its save() method:

form.save()

Then we redirect to the index page. redirect() method is Django shortcut to redirect any URL. Here you can see we write “index” inside redirect() method, it’s the same name we define in our todolist app’s urls.py.

Ok now it’s time to test. Open your browser and write your task and submit it. Then open the admin site and check it.

adding task

task added in database

Here you can see our task is added successfully. Now It’s time to display this tasks in browser.

Retrieve/Display Task

It’s very easy to display all task. Open views.py of todolist app and do change in index function:

from django.shortcuts import render, HttpResponse, redirect
from .forms import TaskForm
from .models import Task

# Create your views here.


def index(request):
    # return HttpResponse("Hello World!!")
    form = TaskForm()

    if request.method == "POST":
        # Get the posted form
        form = TaskForm(request.POST)
        if form.is_valid():
            form.save()
        return redirect("index")

    tasks = Task.objects.all() # add this
    return render(request, "index.html", {"task_form": form, "tasks": tasks})

We already see about objects.all() in part 4. Then we store all objects in tasks variable in passing it to our template. Now open index.html and make the following changes.

{% extends 'base.html' %}

{% block title %}
Tasks - 
{% endblock title %}

{% block content %}
    <!-- <h1>Hello, world!</h1> -->

    <form method="post">
    {% csrf_token %}

        {{task_form}} <!-- variable-->

        <button type="submit">SUBMIT</button>
    </form>

    <!-- this changes -->
    <ol>
        {% for task in tasks %}
            <li>{{task.title}}</li>
        {% endfor %}
    </ol>
    
{% endblock content %}

Here loops over each item in an array, making the item available in a context variable. For example, to display a list of task provided in tasks:

Open your browser and refresh the page.

All task are displayed here.

Update Task

Now we edit our tasks like me marked as completed or fix the typo. For that, we make a new function in our views. Open views.py of todolist app and add the following function:

def update_task(request, pk):
    task = Task.objects.get(id=pk)

    form = TaskForm(instance=task)

    if request.method == "POST":
        form = TaskForm(request.POST, instance=task)
        if form.is_valid():
            form.save()
            return redirect("index")

    return render(request, "update_task.html", {"task_edit_form": form})

This is update_task view. This view takes pk argument to retrieve a particular task with the given id/pk.

Then we get a particular task in our form by passing task instance to our form. so we can update it.

Then handle post request and form with instance otherwise item added as new rather than update it, and save(this time update because we pass instance) the task and redirect to index page. Same as creating task function (index function actually).

Now add url pattern in urls.py of todolist app. Open urls.py and add this line:

from django.urls import path
from . import views

urlpatterns = [
    path("", views.index, name="index"),
    path("update/<int:pk>/", views.update_task, name="update_task"),
]

The second pattern takes the pk arguments and is mapped to the update_task view.

Now we pass task id to our view from the template through url. Open index.html and make the following changes.

{% extends 'base.html' %}

{% block title %}
Tasks - 
{% endblock title %}

{% block content %}
    <!-- <h1>Hello, world!</h1> -->

    <form method="post">
    {% csrf_token %}

        {{task_form}} <!-- variable-->

        <button type="submit">SUBMIT</button>
    </form>

    <ol>
        {% for task in tasks %}
            <li>{{task.title}} | <a href="{% url "update_task" task.id %}">Update</a></li>
        {% endfor %}
    </ol>

{% endblock content %}

Here, url tag Returns an absolute path reference (a URL without the domain name) matching a given view and optional parameters. The first argument is a URL pattern name (we mentioned in urls.py name="update_task"). Additional arguments are optional and should be space-separated values that will be used as arguments in the URL. Here we pass task.id to view.

Refresh your browser and now index page look like this:

update link added

Now to handling the update form make a new HTML file in the templates directory called update_task.html.

{% extends 'base.html' %}

{% block title %}
Update Task - 
{% endblock title %}

{% block content %}

<form method="post">
    {% csrf_token %}
    {{task_edit_form}}

    <button type="submit">Update</button>
</form>

{% endblock content %}

Now let’s test it. Just click on the update link of whatever the task you want to edit.

task updating

Here we updating task.

task updated

Here you can see task is updated.

But we added more something here. You noticed when checked task completed we can’t identify in the list which on is completed right? so, we cross out the task it is completed like a real todo list.

Open index.html and make following changes.

{% extends 'base.html' %}

{% block title %}
Tasks - 
{% endblock title %}

{% block content %}
    <!-- <h1>Hello, world!</h1> -->

    <form method="post">
    {% csrf_token %}

        <!-- {{task_form}} to display all -->
        {{task_form.title}} <!-- to display particular field-->

        <button type="submit">SUBMIT</button>
    </form>

    <ol>
        {% for task in tasks %}
            <li>
                 <!-- this change -->
                {% if task.completed == True %}
                    <strike>{{task.title}} </strike>
                {% else %}
                    {{task.title}}
                {% endif %}
                
                | <a href="{% url "update_task" task.id %}">Update</a>
            
            </li>
        {% endfor %}
    </ol>

{% endblock content %}

We use if statement to check if the task is completed or not. If the task is marked as completed or you can say True then the task displayed with a crossed line using <strike>..</strike> else task is displayed as it is.

completed tasks are crossed

Deleting Task

To delete a task we get a task by getting its id and then we delete the object using delete() method. So for that open views.py of todolist app and add the following view.

def delete_task(request, pk):
    task = Task.objects.get(id=pk)
    task.delete()
    return redirect("index")

This view also receives a pk argument. Now we pass task id our view from the template through URL.

Now add url pattern in urls.py of todolist app. Open urls.py and add this line,

from django.urls import path, include
from . import views

urlpatterns = [
    path("", views.index, name="index"),
    path("update/<int:pk>/", views.update_task, name="update_task"),
    path("delete/<int:pk>/", views.delete_task, name="delete_task"), #this
]

Now open index.html and make following changes.

{% extends 'base.html' %}

{% block title %}
Tasks - 
{% endblock title %}

{% block content %}
    <!-- <h1>Hello, world!</h1> -->

    <form method="post">
    {% csrf_token %}

        <!-- {{task_form}} to display all -->
        {{task_form.title}} <!-- to display particular field-->

        <button type="submit">SUBMIT</button>
    </form>

    <ol>
        {% for task in tasks %}
            <li>
                {% if task.completed == True %}
                    <strike>{{task.title}} </strike>
                {% else %}
                    {{task.title}}
                {% endif %}
                
                | <a href="{% url "update_task" task.id %}">Update</a> | <a href="{% url "delete_task" task.id %}">Delete</a> 
            
            </li>
        {% endfor %}
    </ol>

{% endblock content %}

Your index page looks like this:

delete link added

Now test it and delete some tasks.

Okay now all operation done. Your todo app is almost complete.

That’s it for the now. Bootstrap is already added to our template right, so in the next tutorial, we styling our template.

Hope you enjoyed this tutorial. Please share it with your friends it helps your friend as well as me :)

Previous:

Next: