Skip to content

Instantly share code, notes, and snippets.

@SeyedEhsanHosseini
Created October 6, 2023 18:02
Show Gist options
  • Save SeyedEhsanHosseini/25afd8d82bfc23a9a5da8c73fa2ee88a to your computer and use it in GitHub Desktop.
Save SeyedEhsanHosseini/25afd8d82bfc23a9a5da8c73fa2ee88a to your computer and use it in GitHub Desktop.
Django-Simple-ToDoList

ToDo Project

Step 0

Make a README.md file

Create an appropriate .gitignore file from [https://www.toptal.com/developers/gitignore]

Step 1

Create a virtual environment and activate it to install dependencies

python3 -m venv venv && source venv/bin/activate

Install dependencies

pip install django djangorestframework

Freeze the installed packages in requirements.txt file

pip freeze > requirements.txt

Step 2: Start Django project

django-admin startproject todo
cd todo/
python3 manage.py startapp todolist

Step 3: Modify settings.py

from

ALLOWED_HOSTS = []

to

ALLOWED_HOSTS = ['*']

from

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

to

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'todolist',
    'rest_framework',
]

from

TIME_ZONE = 'UTC'

to

TIME_ZONE = 'Asia/Tehran'

Step 4: Create the model

In "todolist/models.py" file

from django.db import models

from django.utils import timezone

class Task(models.Model):
    STATUS_CHOICES = (
        ('BL', 'Backlog'),
        ('IP', 'In-progress'),
        ('DN', 'Done'),
    )

    title = models.CharField(max_length=200)
    description = models.TextField()
    status = models.CharField(max_length=2, choices=STATUS_CHOICES, default='BL')
    completed_at = models.DateTimeField(blank=True, null=True)
    created_at = models.DateTimeField(auto_now_add=True)

    def save(self, *args, **kwargs):
        if self.status == 'DN' and not self.completed_at:
            self.completed_at = timezone.now()
        elif self.status != 'DN':
            self.completed_at = None
        super().save(*args, **kwargs)

After defining the models, you need to create and apply migrations to update the database schema accordingly.

Run the following commands:

python3 manage.py makemigrations

python3 manage.py migrate

Register Task model to admin panel in "todolist/admin.py" file

from .models import Task

admin.site.register(Task)

Step 5: Create the serializers and views for CRUD operation

You'll need to create a serializer class for the Task model to handle serialization and deserialization of the data.

Make a newfile called "serializers.py"

touch todolist/serializers.py

Add below lines to it :

from rest_framework import serializers
from .models import Task

class TaskSerializer(serializers.ModelSerializer):
    class Meta:
        model = Task
        fields = '__all__'

In "todolist/views.py" file

Import necessary modules:

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status

from .models import Task

from .serializers import TaskSerializer

Create an API view for creating a task:

class TaskCreateAPI(APIView):
    def post(self, request):
        serializer = TaskSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

Create an API view for getting a list of tasks:

class TaskListAPI(APIView):
    def get(self, request):
        tasks = Task.objects.all()
        serializer = TaskSerializer(tasks, many=True)
        return Response(serializer.data)

Create an API view for getting,updating or deleting a specific task:

class TaskDetailAPI(APIView):   
    def get_task(self, pk):
        try:
            return Task.objects.get(pk=pk)
        except Task.DoesNotExist:
            raise status.HTTP_404_NOT_FOUND

    def get(self, request, pk):
        task = self.get_task(pk)
        serializer = TaskSerializer(task)
        return Response(serializer.data)

    def patch(self, request, pk):
        task = self.get_task(pk)
        serializer = TaskSerializer(task, data=request.data, partial=True)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    def delete(self, request, pk):
        task = self.get_task(pk)
        task.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)

In the above code, we defined our API views:

TaskCreateAPI handles the creation of a new task using the HTTP POST method.

TaskListAPI retrieves a list of all tasks using the HTTP GET method.

TaskDetailAPI retrieves, updates, or deletes a specific task using its primary key (pk). It implements the HTTP GET, PATCH, and DELETE methods.

Step 6: Configure the URL routing in our urls.py files to map these API views to the desired URLs.

In project urls.py file:

from this

from django.contrib import admin
from django.urls import path

urlpatterns = [
    path('admin/', admin.site.urls),
]

to this

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/', include('todolist.urls'))
]

Create app urls.py file:

touch todolist/urls.py
from django.urls import path
from .views import TaskCreateAPI, TaskListAPI, TaskDetailAPI

urlpatterns = [
    path('tasks/', TaskListAPI.as_view(), name='task-list'),
    path('tasks/create/', TaskCreateAPI.as_view(), name='task-create'),
    path('tasks/<int:pk>/', TaskDetailAPI.as_view(), name='task-detail'),
]

In the above code, we define three URL patterns:

"tasks/": This pattern matches the endpoint for retrieving a list of tasks. It maps to the TaskListAPI view through the as_view() method. You can access the list of tasks using /api/tasks/ URL.

"tasks/create/": This pattern matches the endpoint for creating a new task. It maps to the TaskCreateAPI view through the as_view() method. You can use the //api/tasks/create/ URL to create a new task.

"tasks/int:pk/": This pattern matches the endpoint for retrieving, updating, or deleting a specific task. The int:pk captures the primary key (pk) of the task. It maps to the TaskDetailAPI view. You can access a specific task using /api/tasks/<task_id>/, where <task_id> is the primary key of the task.

Step 7: Create a superuser

python3 manage.py createsuperuser

Step 8: Run project and check access to admin panel :

python3 manage.py runserver

Open below address in your browser:

http://localhost:8000/admin/

Step 9: Test the API using some curl requests :

Create a task:

curl -X POST -H "Content-Type: application/json" -d '{"title": "Task 1", "description": "Task description", "status": "BL"}' http://localhost:8000/api/tasks/create/

Get a list of tasks:

curl -X GET http://localhost:8000/api/tasks/

Get a specific task: (Replace <task_id> with the actual primary key of a task.)

curl -X GET http://localhost:8000/api/tasks/<task_id>/

Update a specific task (PATCH request):

curl -X PATCH -H "Content-Type: application/json" -d '{"title": "Updated Task 1"}' http://localhost:8000/api/tasks/<task_id>/

curl -X DELETE http://localhost:8000/api/tasks/<task_id>/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment