Lesson 3
Creating the To-Do Model
Introduction

Welcome to the lesson on creating the To-Do model in Django. In this lesson, we will focus on building the foundational model for our To-Do application. The goal is to help you understand how to define a model in Django, set up corresponding views, and map these views to URLs so that you can create and display To-Do items via an API endpoint.

Models in Django are used to define the structure of your database tables. By the end of this lesson, you will be able to create a Todo model with fields for the task description and its completion status and expose this data through an API that you can test.

Creating the To-Do Model

In Django, a model defines the structure of your database tables. Each model is a Python class that subclasses django.db.models.Model. It contains fields that represent the columns of the table. Let's create our Todo model.

Here is the code for creating the basic Todo model in models.py:

Python
1from django.db import models 2 3class Todo(models.Model): 4 task = models.CharField(max_length=200) 5 completed = models.BooleanField(default=False) 6 7 def __str__(self): 8 return self.task
  • task: This field is a CharField, which is used to store text data. Note that CharField must have a max_length parameter. We set the max_length to 200, which means this field can store up to 200 characters.
  • completed: This field is a BooleanField that stores a True or False value. We set the default value to False, meaning that a task is not completed by default.
  • __str__(self): This method returns the model's string representation. It is used when you print an instance of the model, or when the instance is displayed in the Django admin interface or Django shell. As we won't need it in the first courses of this path, we define it as simply the task.name. However, in your project, you can come up with any __str__ method behaviour you find comfortable.

This model represents an SQL table for storing tasks. The columns of this table will be id, task, and completed. Note that we don't need to define the id column manually, Django will create it automatically.

Here is an illustration of how the SQL table would look:

idtaskcompleted
1Example Task 1False
2Example Task 2True
Common Field Types in Django Models

To give you a broader understanding, here's a table explaining common Django model fields and their corresponding Python types:

Django FieldPython TypeDescription
CharFieldstrUsed to store text data with a maximum length.
BooleanFieldboolUsed to store True/False values.
IntegerFieldintUsed to store integer values.
FloatFieldfloatUsed to store floating-point numbers.
DateFielddatetime.dateUsed to store dates (without time).
DateTimeFielddatetime.datetimeUsed to store date and time.
TextFieldstrUsed to store large text data.
EmailFieldstrUsed to store email addresses.

By understanding these field types, you can design your database tables to suit various kinds of data that your application might need to handle.

Applying Migrations

Once we define the model, we need to create the corresponding database table. Django uses a system called migrations to handle database schema changes. Here’s how to generate and apply migrations for our Todo model.

First, generate the migration for the Todo model by running:

1python manage.py makemigrations

This command will create a new migration file in the migrations directory of your app. The migration file contains the instructions to create the table for the Todo model. You can check the migrations directory to see the newly created migration file, which will have a name like 0001_initial.py.

Next, apply the migration to create the table in the database by running:

1python manage.py migrate

This command will apply all pending migrations, including the one for the Todo model. Now, the database should have a new table corresponding to the Todo model, ready to store tasks.

By using migrations, we can easily manage changes to the database schema over time, and Django ensures that our database always matches our models.

Important Note: Whenever you make any changes in the model's configuration, including adding a new field, adding a new model, or even slightly adjusting a field parameter, you should make and apply migrations to reflect this in the database. Pay attention to this when solving tasks, as forgetting to apply migrations is a common mistake.

Potential Problems

When adding a new field to a model, Django may prompt you to provide a default value for migrations to handle existing rows without this field.

For instance, if adding a priority field to the Todo model, you may see:

1You are trying to add a non-nullable field 'priority' to todo without a default.

There are three ways to address this issue:

  1. One-off Default Value: Provide a default value during the migration prompt. This sets the specified value for all existing rows temporarily. It can be any valid python value.

  2. Add Default in Models: Update your model to include a default value for the new field:

    Python
    1priority = models.IntegerField(default=1)

    This applies the default to all future objects as well. After updating, run makemigrations and migrate.

  3. Allow Null Values: Set null=True for the new field to permit existing rows without a default value:

    Python
    1priority = models.IntegerField(null=True)

These options help in managing database schema updates while keeping data consistent.

Adding Views for the To-Do Model

We will create a view to list all To-Do items available in our database.

Here is the code for the todo_list view in views.py:

Python
1from rest_framework.views import APIView 2from rest_framework.response import Response 3from .models import Todo 4 5class TodoList(APIView): 6 def get(self, request): 7 todos = Todo.objects.all().values('task', 'completed') 8 return Response({"data": todos})
  • get(self, request): This method handles GET requests.
  • Todo.objects.all().values('task', 'completed'): This line fetches all To-Do objects from the database and retrieves the values for the task and completed fields.
  • Finally, the method returns a Response with all the retrieved items.

Using this view, we can fetch all To-Do items and display them in a JSON format.

Setting Up URLs for To-Do Views

URL routing in Django maps URL patterns to views. We need to set up the URL configuration for our To-Do API.

Here is the code for adding the URL pattern in urls.py:

Python
1# project/myapp/urls.py 2from django.urls import path 3from .views import TodoList 4 5urlpatterns = [ 6 path('todos/', TodoList.as_view()), 7]

The path('todos/', TodoList.as_view()) line maps the URL path /todos/ to the todo_list view.

With this URL configuration in place, our new view will be accessible at the /todos/ endpoint.

Testing the To-Do API: Part 1

To ensure our To-Do API works correctly, we will test it using the requests library. Below is the code for testing the To-Do API using a simple script in test_script.py:

Python
1import requests 2 3URL = 'http://0.0.0.0:3000/api/todos/' # Ensure the URL matches your endpoint 4response = requests.get(URL) 5print(response) # {'data': []}

By now, there should be no TODOs in the database, so we will get an empty list.

Creating Objects

We will cover creating new objects in deep detail in the next course. However, let's implement a quick way to send post requests so we can test our app.

Here is the code for the updated TodoList view in views.py:

Python
1from rest_framework.views import APIView 2from rest_framework.response import Response 3from rest_framework import status 4from .models import Todo 5 6class TodoList(APIView): 7 def get(self, request): 8 todos = Todo.objects.all() 9 return Response({"data": todos}) 10 11 def post(self, request): 12 task = request.data.get('task') 13 completed = request.data.get('completed', False) 14 todo = Todo.objects.create(task=task, completed=completed) 15 return Response({"data": {"task": todo.task, "completed": todo.completed}}, status=status.HTTP_201_CREATED)

The new post method allows us to send requests with some new task data to create new objects. Let's break it down.

  • post(self, request): This method handles POST requests.
  • request.data.get('task'): This retrieves the task value from the request data.
  • request.data.get('completed', False): This retrieves the completed value from the request data; if not provided, it defaults to False.
  • Todo.objects.create(task=task, completed=completed): This creates a new To-Do item in the database.
  • status=status.HTTP_201_CREATED: This indicates that a new resource has been created successfully, which is the standard status code for a successful POST request in REST APIs.

The Response function from rest_framework.response is used to generate an HTTP response with the given data and status code. For POST methods, it serializes the returned data into JSON format and sends it back to the client.

Testing the To-Do API: Part 2

Now, we can test both POST and GET requests with our testing script:

Python
1import requests 2 3URL = 'http://0.0.0.0:3000/api/todos/' # Ensure the URL matches your endpoint 4 5# Create a new To-Do item 6new_todo = {'task': 'Learn Django', 'completed': False} 7post_response = requests.post(URL, json=new_todo) 8print(post_response.json()) # Expect to see the newly created TODO item 9 10# Fetch all To-Do items 11get_response = requests.get(URL) 12print(get_response.json()) # Expect to see a list containing the new TODO item
Summary and Next Steps

In this lesson, we covered the following steps to create our To-Do model and expose it through an API:

  1. Recapped the initial setup of the Django project and app.
  2. Created the Todo model to store task descriptions and completion status.
  3. Added a view to fetch and respond with the To-Do items.
  4. Set up URL routing to map the view to an endpoint.
  5. Tested the To-Do API using a simple script.

Now, you are ready to practice these steps on your own using the CodeSignal IDE. Continue with the practice exercises to reinforce what you’ve learned and ensure you’re comfortable with building models, views, and URL configurations in Django. Happy coding!

Enjoy this lesson? Now it's time to practice with Cosmo!
Practice is how you turn knowledge into actual skills.