Skip to content

sivanov/api-django

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

12 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Backedn REST API TODO App with Django 4 and Django Rest Framework (DRF)

This project ir related with : Svelte Front-end App

Setting up Django Backend

Notice: Commands used in this tutorial are executed under Linux Mint and are not tested under Windows and MAC OS

Open a new terminal window and run the following command to create a new project directory:

mkdir api-django

Navigate inside the directory

cd api-django

Good practice is for each new project to using isolated virtual environment lik "venv". venv is the standard tool for creating virtual environments, and has been part of Python since Python 3.3

Generate new venv with command:

python3 -m venv .venv

If you’re using Ubuntu Linux, or any other Debian-based distribution, it’s possible you’ll get the following error message:

The virtual environment was not created successfully because pip is not available

To solve this run :

sudo apt-get install python3-venv

Now repeat again up command:

python3 -m venv .venv

Now we need to activate venv:

source .venv/bin/activate

# on success prompt will look like this:
(venv)$: 

Now is time to install needed python packages with pip3

pip3 install django

Django have handy command that will generate new project for us:

django-admin startproject backend

This directory is root for our Project. Each Django project cant hav multiple application inside this directory.

navigate into the newly created backend dir

cd backend

Start a new application called todo(inside venv it doesn't mether if commands are with python or python3):

python manage.py startapp todo
or
python3 manage.py startapp todo

now we have dir for our first Application:

Django have another handy command that will generate new Database for us:

python manage.py migrate

In terminal we will see:

python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, sessions
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying admin.0003_logentry_add_action_flag_choices... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying auth.0008_alter_user_username_max_length... OK
  Applying auth.0009_alter_user_last_name_max_length... OK
  Applying auth.0010_alter_group_name_max_length... OK
  Applying auth.0011_update_proxy_permissions... OK
  Applying auth.0012_alter_user_first_name_max_length... OK
  Applying sessions.0001_initial... OK

This command will create file "db.sqlite3" this is out database

Now is time to run and see our new backend website:

python manage.py runserver

Output in terminal will be:

python manage.py runserver
Watching for file changes with StatReloader
Performing system checks...

System check identified no issues (0 silenced).
May 28, 2022 - 19:10:43
Django version 4.0.4, using settings 'backend.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.

click over URL:http://127.0.0.1:8000/ or just open it in browser

If all is OK you can stop the server with keys combination CTRL+C.

Because it's god practice each project to be track witt git version control we need to create new file with name ".gitignore" in our root directory "api-django":

# Environments 
.venv 

# Django #
*.log
*.pot
*.pyc
__pycache__

This will reduce cont of file in our github repo and will make our project more reusable. Notice files and folders with grey color, this mean that they are not included anymore in git version control.

Adding TODO application in our project

Now is time to continue work over our fist Django application Open file "backend/settings.py" and find section "Application definition" and add new finale line like this

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'todo' # our first Django Application
]

Save change to file.

Creating Model

A model is the single, definitive source of information about your data. It contains the essential fields and behaviors of the data you’re storing. Generally, each model maps to a single database table. source: docs.djangoproject.com

Open and edit file: backend/todo/models.py

from pickle import FALSE
from django.db import models

class Todo(models.Model):
    # we can write max 200 characters in Title field
    title = models.CharField(max_length = 200)
    description = models.TextField()
    # by default all new created task will be uncompleted
    completed = models.BooleanField(default = False)

    def __str__(self):
        return self.title

Now is time to ask Django to create some changes for us in database. We using migrations for for this purpose. Migrations are Django’s way of propagating changes you make to your models (adding a field, deleting a model, etc.) into your database schema. They’re designed to be mostly automatic, but you’ll need to know when to make migrations, when to run them, and the common problems you might run into.source: docs.djangoproject.com

Command makemigrations which is responsible for creating new migrations based on the changes you have made to your models.

python manage.py makemigrations todo

terminal output should be like:

Migrations for 'todo':
  todo/migrations/0001_initial.py
    - Create model Todo

this command will generate for us file: backend/todo/migrations/0001_initial.py

Now with next command we will apply all new changes in database or migrate is responsible for applying and unapplying migrations.:

python manage.py migrate todo

terminal output:

Operations to perform:
  Apply all migrations: todo
Running migrations:
  Applying todo.0001_initial... OK

To work our TODO app we need to use some type of user interface and now is time to create it. Django have build in Admin tool and dashboard for this type of operations.

Open and edit file: backend/todo/admin.py like this:

from django.contrib import admin
from .models import Todo # Django now cant import our Model file

class TodoAdmin(admin.ModelAdmin):
    #  what fields will be visible in our Admin dashboard
    list_display = ('title', 'description', 'completed')

# Register your models here.
admin.site.register(Todo, TodoAdmin)

Creating Administrator user and password

python manage.py createsuperuser

You will be prompted to enter a username, email, and password for the superuser. Please re,e,ber username and password. In our test case we will use: 'admin' and 'asdf'. Notice: never use short password in production websites.

Terminal output:

Username (leave blank to use 'ivanov'): admin
Email address: 
Password: 
Password (again): 
This password is too short. It must contain at least 8 characters.
This password is too common.
Bypass password validation and create user anyway? [y/N]: y
Superuser created successfully.

Now is time to run local webserver and see what we have done.

python manage.py runserver

Open in browser http://localhost:8000/admin

Log in with the username and password that we create before that

Now is time to add some test task to see if website work. Add some task for test.

Creating API using Django REST Framework

In this example we will use django-rest-framework.org and django-cors-headers. django-cors-headers adds Cross-Origin Resource Sharing (CORS) headers to responses. This allows in-browser requests to your Django application from other origins in our case from localhost

If server is still running you must stop is with keys combination CRL + C and then install new required packages:

pip3 install djangorestframework django-cors-headers

Open and edit file: backend/settings.py

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'todo', # our first Django Application
    'corsheaders',
    'rest_framework',
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'corsheaders.middleware.CorsMiddleware',
]

Then add this lines to the bottom of the backend/settings.py file:

CORS_ORIGIN_WHITELIST = [
    'http://localhost:3000'
]

Convert model instances to JSON - creating serializers

Serializers will send JSON data to any frontend. In todo folder create new file with name serializers.py. Path must be like this: backend/todo/serializers.py

# backend/todo/serializers.py
from rest_framework import serializers
from .models import Todo

class TodoSerializer(serializers.ModelSerializer):
    class Meta:
        model = Todo
        # fields contained and converted in JSON data
        fields = ('id', 'title', 'description', 'completed')

To be more easy our development we need to create View. Open file todo/views.py:

from django.shortcuts import render
# base class provides the implementation for CRUD operations by default
from rest_framework import viewsets
from .serializers import TodoSerializer
from .models import Todo

class TodoView(viewsets.ModelViewSet):
    serializer_class = TodoSerializer
    queryset = Todo.objects.all()

API URLs

Final step is to define URL paths for our API. Open file backend/urls.py:

# backend/urls.py
from django.contrib import admin
from django.urls import path, include
from rest_framework import routers
from todo import views


router = routers.DefaultRouter()
router.register(r'todos', views.TodoView, 'todo')


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

run server:

python manage.py runserver

Open browser and visit: http://127.0.0.1:8000/api/v1/todos/ - list all TODO tasks

http://127.0.0.1:8000/api/v1/todos/1- show only TODO with ID 1

visual browser for SQLite DB

sqlitebrowser.org is visual tool that will help you to understand how information is stored in SQLite 3 DB. Download and run program. Open file db.sqlite3 located in our project.

Alternative variant will be if you use VSC editor and need to download extension: https://marketplace.visualstudio.com/items?itemName=qwtel.sqlite-viewer

Adding Category field in TODO app

Open and edit file:

# Notice this Class must be placed before class Todo
# Category table that inherits model.Models
class Category(models.Model):
    name = models.CharField(max_length=100)

    class Meta:
        verbose_name = ("Category")
        verbose_name_plural = ("Categories")

    def __str__(self):
        return self.name # name to be shown when called


class Todo(models.Model):
.....

Notice: It is important to create from Admin Dashboard first default category and after that to connect model Category with model Todo

creating new migrations based on the changes you have made to your models

python manage.py makemigrations

Terminal output:

Migrations for 'todo':
  todo/migrations/0002_category.py
    - Create model Category

To apply changes in Database

python manage.py migrate

terminal output:

Operations to perform:
  Apply all migrations: admin, auth, contenttypes, sessions, todo
Running migrations:
  Applying todo.0002_category... OK

Ok, now we need to add new model to be visible in Admin Dashboard. open file: backend/todo/admin.py to look like this:

from django.contrib import admin
from .models import Category, Todo # Django now cant import our Model file

class TodoAdmin(admin.ModelAdmin):
    #  what fields will be visible in our Admin dashboard
    list_display = ('title', 'description', 'completed')

class CategoryAdmin(admin.ModelAdmin):
    list_display = ('name',)

# Register your models here.
admin.site.register(Todo, TodoAdmin)
admin.site.register(Category, CategoryAdmin)

start server

python manage.py  runserver

Open in browser http://localhost:8000/admin Now you can see new section with name 'Category'. Add new category with name 'Default'

Now stop server with Ctr + C. Open and edit file: backend/todo/models.py

class Todo(models.Model):
    # we can write max 200 characters in Title field
    title = models.CharField(max_length = 200)
    description = models.TextField()
    # by default all new created task will be uncompleted
    completed = models.BooleanField(default = False)
    # Foreignkey
    category = models.ForeignKey(Category, on_delete=models.PROTECT, default = 1)

With on_delete=models.PROTECT we tell to Django is by some reason some category is deleted, to protect our task and to assign to him default value with id 1.It is good practice to not change this Default category name.

Now it is time to update our migration with command:

python manage.py makemigrations

result:

Migrations for 'todo':
  todo/migrations/0003_todo_category.py
    - Add field category to todo

and to apply changes in DB

python manage.py migrate

result:

Operations to perform:
  Apply all migrations: admin, auth, contenttypes, sessions, todo
Running migrations:
  Applying todo.0003_todo_category... OK

Start again webserver:

python manage.py  runserver

Open in browser http://localhost:8000/admin and if you open some of task they will have populated default category now.

Feel fre to add new catagories and to update existing task with them.

Exposing new field Category in API JSON Data

Open and edit file: backend/todo/serializers.py and add 'category',

fields = ('id', 'title', 'category', 'description', 'completed')

If you open browser on http://127.0.0.1:8000/api/v1/todos/ filed must exist in JSOn Data

Show category name instead of Database ID

Open and edit file and 2 rows one with comment and this after him:

class TodoSerializer(serializers.ModelSerializer):
    # replace category ID in Database with actual human readable name
    category = serializers.CharField(source="category.name", read_only=True)

Add priority field

For simplicity range for priority will be from intigers from 1 to 10 scale. By this way you no need to remember words related with prioriry level and query to DB will be more simple for now. Open and edit file: backend/todo/models.py

class Todo(models.Model):
    PRIORITY_LEVELS = (
        ('1', '1'),
        ('2', '2'),
        ('3', '3'),
        ('4', '4'),
        ('5', '5'),
        ('6', '6'),
        ('7', '7'),
        ('8', '8'),
        ('9', '9'),
        ('10', '10'),
    )
python manage.py makemigrations

Output:

Migrations for 'todo':
  todo/migrations/0004_todo_priority.py
    - Add field priority to todo
python manage.py migrate

output:

Operations to perform:
  Apply all migrations: admin, auth, contenttypes, sessions, todo
Running migrations:
  Applying todo.0004_todo_priority... OK

Is webserver is not running start it with:

python manage.py  runserver

open Admin dashboard: http://127.0.0.1:8000/admin/todo/todo/1/change/

If you check all existing task they will be filled with default value "1"

Now is time this filed to be visible in API JSON data and Djnago buy default dont show new fields unless you explicitly do so.

Search inside title and description

In some meoment you will have lagre task list in TODO App and we need to have search functionallity.

Open and edit file like this:

from django.shortcuts import render
# base class provides the implementation for CRUD operations by default
from rest_framework import viewsets
from .serializers import TodoSerializer
from .models import Todo
# for search 
from rest_framework import filters
from rest_framework import generics

class TodoView(viewsets.ModelViewSet):
    serializer_class = TodoSerializer
    queryset = Todo.objects.all()

# http://127.0.0.1:8000/api/v1/q/?search=todo
class TodoSearchListView(generics.ListAPIView):
    queryset = Todo.objects.all()
    serializer_class = TodoSerializer
    filter_backends = [filters.SearchFilter]
    # search in 1 or more field in DB. Required to be CharField or TextField.
    search_fields = ['title', 'description']

To be acesable our serch URL we need to add it inside file: backend/backend/urls.py

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/v1/', include(router.urls)),
    # http://127.0.0.1:8000/api/v1/q/?search=todo
    path('api/v1/q/', views.TodoSearchListView().as_view())
]

Open URL http://127.0.0.1:8000/api/v1/q/?search=

To test search by some world inside title or description witype after "=" some text for test for example like this http://127.0.0.1:8000/api/v1/q/?search=todo

Waht will be result:

Statistics for site-packages count and size

.venv/share/python-wheels 27 items, totalling 2,2 MB
.venv/lib/python3.8/site-packages: 7 637 items, totalling 41,8 MB

After installing django-rest-framework and django-cors-headers:

.venv/lib/python3.8/site-packages: 8 775 items, totalling 46,7 MB

About

Detailed tutorial how to create multi Django Applications and backedn with API

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages