Hello! Welcome back to your course on building a full-stack web application with Python and Django.
In our last lesson, you did the foundational work of designing and creating the Task model, which now serves as the blueprint for your application's data. You've successfully created the database table and even tested it using the Django admin.
Today, we'll build on that foundation to implement a core feature of your Task Manager: allowing users to create new tasks directly from the website. This lesson corresponds to the learning outcome: Build the 'Create' functionality for tasks using CreateView. We will be bringing the "C" (Create) from CRUD (Create, Read, Update, Delete) to life for your users.
Imagine your final application dashboard:

Let's get started.
From Model to Form: Introducing CreateView
So far, the only way to create a task is through the admin panel, which isn't accessible to your end-users. We need to provide a public-facing form. While you could build this from scratch with a function-based view, Django provides a powerful shortcut: Class-Based Views (CBVs).
Specifically, we'll use CreateView. This generic view does most of the heavy lifting for you:
- It displays a form based on your
Taskmodel. - It handles form validation when the user submits data.
- It saves the new object to the database upon successful validation.
- It redirects the user to a success page.
This approach saves you from writing a lot of repetitive code and is a standard practice in modern Django development.
To get a better sense of what CreateView is and why it's so useful, watch this short theoretical overview.
Learn Django Class Based Views - CreateView - Theory and Examples
The video 'Learn Django Class Based Views - CreateView - Theory and Examples' from Very Academy provides an excellent, concise explanation of what CreateView does and how it differs from other views.
Watch from 00:00:32 to 00:02:28. Focus on understanding the primary purpose of CreateView—to create and save a new object—and how it simplifies the process compared to more manual methods.
Step 1: Implementing the TaskCreate View
Now, let's write the code. Open the views.py file in your task application's directory. We will create a class that inherits from CreateView.
Add the following code:
from django.views.generic.edit import CreateView
from django.urls import reverse_lazy # Used to redirect the user after a successful form submission
from .models import Task
class TaskCreate(CreateView):
model = Task
fields = ['title', 'description']
success_url = reverse_lazy('tasks') # We'll create a URL named 'tasks' in the next lesson
Let's break down these attributes:
model = Task: This tellsCreateViewwhich model it should use to create an object. Django will introspect this model to build the form.fields = ['title', 'description']: This specifies which fields from theTaskmodel should be included in the form. Notice we didn't includeuser,completed, orcreated. Why?userwill be set automatically based on the logged-in user.completedhas a default value ofFalse.createdis set automatically withauto_now_add=True.
success_url = reverse_lazy('tasks'): After a user successfully creates a task, this is where they will be redirected.reverse_lazyis the recommended way to specify URLs for CBVs; it looks up the URL pattern by itsname. We'll create the'tasks'URL pattern in our next lesson, which will show the list of all tasks.
Step 2: Creating the URL Pattern
Your application now has a view, but there's no way to access it from a browser. We need to map a URL to it.
-
If you don't already have one, create a new file named
urls.pyinside your app's directory. -
Add the following URL pattern to this file:
# In your_app/urls.py from django.urls import path from .views import TaskCreate # Import your new view urlpatterns = [ path('task-create/', TaskCreate.as_view(), name='task-create'), # You might have other URLs here, and will add more later ] -
Ensure your app's
urls.pyis included in your project's mainurls.pyfile (the one in yourmysiteor project folder). It should look something like this:# In your_project/urls.py from django.contrib import admin from django.urls import path, include urlpatterns = [ path('admin/', admin.site.urls), path('', include('your_app.urls')), # Includes all URLs from your app ]
The .as_view() method is what converts your TaskCreate class into a callable view that Django can use to handle requests.
Step 3: Building the HTML Template
CreateView needs an HTML template to render the form. By default, it looks for a template named <model_name>_form.html. In our case, that's task_form.html.
-
In your app directory, create a
templatesfolder if it doesn't exist. Insidetemplates, create another folder with the same name as your app (e.g.,your_app/templates/your_app/). -
Inside that folder, create a new file named
task_form.htmland add the following code:<h1>Create a new Task</h1> <form method="post"> {% csrf_token %} {{ form.as_p }} <input type="submit" value="Create Task"> </form>
Let's examine the key parts:
<form method="post">: The method must bepostto send data to the server.{% csrf_token %}: This is a crucial security tag that protects against Cross-Site Request Forgery attacks. Django requires it for allPOSTforms.{{ form.as_p }}: This is where the magic happens. TheCreateViewpasses a form object to the template. This tag renders the form's fields (titleanddescription) as a series of paragraph (<p>) tags.
This entire workflow—from setting up the view to creating the template—is shown very clearly in the following video resource.
Django CRUD Functionality with CBVS CLASS BASED VIEWS
The video 'Django CRUD Functionality with CBVS' by The Zuri Team provides a full, practical walkthrough of setting up a CreateView from scratch. It covers creating the model, form, view, URL, and template.
Watch from 00:15:20 to 00:30:15. This segment focuses specifically on implementing CreateView. Pay close attention to how the view, URL, and template all work together. The video builds a simple to-do app, which is very similar to what you are doing.
Step 4: The Crucial Final Piece - Associating Tasks with Users
We have one major problem left. If you run the server now and try to create a task, it will fail. The Task model requires a user, but our form doesn't ask for one, nor does our view know who is logged in.
We need to modify our TaskCreate view to:
- Require the user to be logged in to access this page.
- Automatically set the
userof the new task to the currently logged-in user.
The article "Get Done ✅" provides a perfect example of how to do this.
Get Done ✅ : A step-by-step guide in building a Django To ...
This guide provides a concise and practical example of a CreateView that correctly handles user association, which is exactly what we need.
In the article, find the section 'Step 4: Define Views' and look at the TaskCreate class. Notice two key additions: LoginRequiredMixin and the form_valid method. We will now apply these to our own code.
Let's update views.py with this new logic:
# your_app/views.py
from django.views.generic.edit import CreateView
from django.urls import reverse_lazy
from django.contrib.auth.mixins import LoginRequiredMixin # Import the mixin
from .models import Task
class TaskCreate(LoginRequiredMixin, CreateView): # Add LoginRequiredMixin
model = Task
fields = ['title', 'description']
success_url = reverse_lazy('tasks')
def form_valid(self, form):
form.instance.user = self.request.user # Set the user of the new task
return super().form_valid(form)
Changes Explained:
LoginRequiredMixin: This is a special class you add to your view's inheritance list. It automatically checks if the user is logged in. If not, it redirects them to the login page.def form_valid(self, form)::CreateViewcalls this method after the submitted form data has been successfully validated. We are overriding it to add our custom logic before the object is saved.form.instance.user = self.request.user: This is the key line.form.instanceis theTaskobject that is about to be saved. We set itsuserattribute toself.request.user, which is theUserobject for the currently logged-in user.return super().form_valid(form): After setting the user, we call the originalform_validmethod from the parent class to ensure the object is saved to the database as intended.
Now, run your server (python manage.py runserver), log in, and navigate to http://127.0.0.1:8000/task-create/. You can create a task, and if you check the admin, you'll see it is correctly assigned to your user!
Test your understanding!
What would happen if you removed LoginRequiredMixin from the TaskCreate view and a user who was not logged in tried to submit the form to create a task? Why?
Show answer
The application would raise an error. The form_valid method attempts to execute form.instance.user = self.request.user. For a non-logged-in user, self.request.user is an instance of AnonymousUser, not a regular User object. The database cannot assign an AnonymousUser to the ForeignKey field in the Task model, leading to a ValueError or similar integrity error. This highlights why LoginRequiredMixin is essential for views that interact with user-specific data.
Conclusion
Fantastic work! You have successfully implemented one of the most common and important features of a web application. You've gone beyond the basics by not only creating a form with CreateView but also by ensuring data integrity and security by associating each new task with the correct, authenticated user.
Key Takeaways:
CreateViewis a powerful CBV for handling object creation, minimizing boilerplate code.- You configure
CreateViewby setting attributes likemodel,fields, andsuccess_url. - The view, URL, and template work together: the URL routes to the view, and the view renders the template with a form.
LoginRequiredMixinis essential for protecting views that should only be accessible to authenticated users.- Overriding the
form_validmethod is the standard way to inject custom logic, such as setting the owner of an object, before it's saved.
Preview of the Next Lesson:
Creating tasks is great, but users also need to see the tasks they've created. In the next lesson, we will tackle the "R" in CRUD. You will build the 'Read' functionality to list all of a user's tasks using ListView, creating the very page that our success_url is pointing to.