Skip to content

Session 7 WebApp Backend

Maximiliano Militzer edited this page Apr 7, 2025 · 1 revision

Session 7: WebApp - Backend (Django Ninja)

Overview

Welcome to Session 7! In this session, we'll focus on building the backend of our web application using Django and Django Ninja. Django is a high-level Python web framework that encourages rapid development and clean, pragmatic design. Django Ninja is a modern, fast, and async-ready API framework for Django that makes it easy to build REST APIs.

Learning Objectives

  • Understand the basics of Django and its architecture
  • Learn how to set up a Django project with Django Ninja
  • Create API endpoints for our Todo application
  • Implement data models and database interactions
  • Handle authentication and permissions
  • Test API endpoints

Topics Covered

1. Introduction to Django

Django is a powerful web framework that follows the "batteries-included" philosophy, providing many built-in features for common web development tasks.

Key Features:

  • ORM (Object-Relational Mapper): Interact with your database using Python instead of SQL
  • Admin Interface: Auto-generated admin panel for managing your data
  • Authentication System: Built-in user authentication and authorization
  • URL Routing: Map URLs to views elegantly
  • Template Engine: Create dynamic HTML
  • Form Handling: Process and validate form data
  • Security Features: Protection against common web attacks

2. Setting Up a Django Project

# Create a virtual environment
python -m venv venv
source venv/bin/activate  # On Windows: venv\Scripts\activate

# Install Django and Django Ninja
pip install django django-ninja

# Start a new Django project
django-admin startproject todo_backend
cd todo_backend

# Create a new Django app
python manage.py startapp tasks

# Run migrations
python manage.py migrate

# Create a superuser for the admin panel
python manage.py createsuperuser

# Run the development server
python manage.py runserver

Project Structure:

todo_backend/
├── todo_backend/        # Project folder
│   ├── __init__.py
│   ├── asgi.py
│   ├── settings.py      # Project settings
│   ├── urls.py          # URL configuration
│   └── wsgi.py
├── tasks/               # App folder
│   ├── __init__.py
│   ├── admin.py         # Admin configuration
│   ├── api.py           # API endpoints (Django Ninja)
│   ├── apps.py
│   ├── models.py        # Data models
│   ├── tests.py         # Unit tests
│   └── views.py         # View functions
├── manage.py            # Django command-line utility
└── requirements.txt     # Project dependencies

3. Django Models

Models in Django define the structure of your database tables and allow you to interact with your data using Python code.

# tasks/models.py
from django.db import models
from django.contrib.auth.models import User

class Task(models.Model):
    title = models.CharField(max_length=200)
    description = models.TextField(blank=True)
    completed = models.BooleanField(default=False)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='tasks')
    
    def __str__(self):
        return self.title
    
    class Meta:
        ordering = ['created_at']

After defining your models, create and apply migrations:

python manage.py makemigrations
python manage.py migrate

Register your models with the admin interface:

# tasks/admin.py
from django.contrib import admin
from .models import Task

@admin.register(Task)
class TaskAdmin(admin.ModelAdmin):
    list_display = ('title', 'user', 'completed', 'created_at')
    list_filter = ('completed', 'created_at')
    search_fields = ('title', 'description')

4. Introduction to Django Ninja

Django Ninja is a web framework for building APIs with Django and Python 3.6+ type hints.

Setting up Django Ninja:

# todo_backend/settings.py
INSTALLED_APPS = [
    # ...other apps
    'tasks',
    'ninja',
]

Create an API file:

# tasks/api.py
from ninja import NinjaAPI, Schema
from django.contrib.auth.models import User
from typing import List, Optional
from .models import Task

api = NinjaAPI()

# Schemas for request/response
class TaskSchema(Schema):
    id: int
    title: str
    description: Optional[str] = None
    completed: bool
    created_at: str

class TaskCreateSchema(Schema):
    title: str
    description: Optional[str] = None

# API endpoints
@api.get("/tasks", response=List[TaskSchema])
def list_tasks(request):
    tasks = Task.objects.filter(user=request.user)
    return tasks

@api.post("/tasks", response=TaskSchema)
def create_task(request, payload: TaskCreateSchema):
    task = Task.objects.create(
        title=payload.title,
        description=payload.description,
        user=request.user
    )
    return task

@api.get("/tasks/{task_id}", response=TaskSchema)
def get_task(request, task_id: int):
    task = Task.objects.get(id=task_id, user=request.user)
    return task

@api.put("/tasks/{task_id}", response=TaskSchema)
def update_task(request, task_id: int, payload: TaskCreateSchema):
    task = Task.objects.get(id=task_id, user=request.user)
    task.title = payload.title
    task.description = payload.description
    task.save()
    return task

@api.patch("/tasks/{task_id}/complete", response=TaskSchema)
def complete_task(request, task_id: int):
    task = Task.objects.get(id=task_id, user=request.user)
    task.completed = not task.completed
    task.save()
    return task

@api.delete("/tasks/{task_id}")
def delete_task(request, task_id: int):
    task = Task.objects.get(id=task_id, user=request.user)
    task.delete()
    return {"success": True}

Include the API in your project's URL configuration:

# todo_backend/urls.py
from django.contrib import admin
from django.urls import path
from tasks.api import api

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

5. Authentication with Django Ninja

Django Ninja can work with Django's built-in authentication system:

# tasks/api.py
from ninja import NinjaAPI, Schema
from ninja.security import HttpBearer
from django.contrib.auth.models import User
from django.contrib.auth import authenticate
from django.conf import settings
import jwt
from datetime import datetime, timedelta

class TokenSchema(Schema):
    access_token: str
    token_type: str = "bearer"

class LoginSchema(Schema):
    username: str
    password: str

class AuthBearer(HttpBearer):
    def authenticate(self, request, token):
        try:
            payload = jwt.decode(token, settings.SECRET_KEY, algorithms=["HS256"])
            user_id = payload.get("user_id")
            user = User.objects.get(id=user_id)
            request.user = user
            return user
        except:
            return None

api = NinjaAPI(auth=AuthBearer())

@api.post("/token", auth=None, response=TokenSchema)
def login(request, credentials: LoginSchema):
    user = authenticate(username=credentials.username, password=credentials.password)
    if user is None:
        return api.create_response(request, {"detail": "Invalid credentials"}, status=401)
    
    token_expiry = datetime.utcnow() + timedelta(days=1)
    access_token = jwt.encode(
        {"user_id": user.id, "exp": token_expiry},
        settings.SECRET_KEY,
        algorithm="HS256"
    )
    
    return {"access_token": access_token}

6. Adding Cross-Origin Resource Sharing (CORS)

To allow our frontend to communicate with the backend, we need to configure CORS:

pip install django-cors-headers
# todo_backend/settings.py
INSTALLED_APPS = [
    # ...other apps
    'corsheaders',
]

MIDDLEWARE = [
    'corsheaders.middleware.CorsMiddleware',  # Add this at the top
    # ...other middleware
]

# Allow all origins in development (customize for production)
CORS_ALLOW_ALL_ORIGINS = True

7. Testing Django Ninja APIs

Django provides a built-in testing framework that you can use to test your API endpoints:

# tasks/tests.py
from django.test import TestCase
from django.contrib.auth.models import User
from .models import Task
from django.urls import reverse
import json
import jwt
from django.conf import settings
from datetime import datetime, timedelta

class TaskApiTests(TestCase):
    def setUp(self):
        # Create a test user
        self.user = User.objects.create_user(username='testuser', password='testpassword')
        
        # Create some test tasks
        Task.objects.create(title='Test Task 1', user=self.user)
        Task.objects.create(title='Test Task 2', user=self.user, completed=True)
        
        # Generate token
        token_expiry = datetime.utcnow() + timedelta(days=1)
        self.token = jwt.encode(
            {"user_id": self.user.id, "exp": token_expiry},
            settings.SECRET_KEY,
            algorithm="HS256"
        )
    
    def test_list_tasks(self):
        response = self.client.get(
            '/api/tasks', 
            HTTP_AUTHORIZATION=f'Bearer {self.token}'
        )
        self.assertEqual(response.status_code, 200)
        data = json.loads(response.content)
        self.assertEqual(len(data), 2)
    
    def test_create_task(self):
        payload = {
            'title': 'New Task',
            'description': 'Task description'
        }
        response = self.client.post(
            '/api/tasks',
            data=json.dumps(payload),
            content_type='application/json',
            HTTP_AUTHORIZATION=f'Bearer {self.token}'
        )
        self.assertEqual(response.status_code, 200)
        data = json.loads(response.content)
        self.assertEqual(data['title'], 'New Task')
        
        # Verify task was created in database
        task_exists = Task.objects.filter(title='New Task').exists()
        self.assertTrue(task_exists)

Run the tests with:

python manage.py test

Practice Exercises

  1. Set up a Django project with Django Ninja
  2. Create models for a Todo application
  3. Implement API endpoints for CRUD operations on tasks
  4. Add authentication to your API
  5. Write tests for your API endpoints
  6. Create a user registration endpoint

Additional Resources

Next Steps

Now that you've learned how to build the backend of a web application with Django Ninja, you're ready to move on to Session 8: WebApp - Database where you'll learn how to work with PostgreSQL and implement more advanced database features.

Clone this wiki locally