-
Notifications
You must be signed in to change notification settings - Fork 0
Session 7 WebApp Backend
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.
- 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
Django is a powerful web framework that follows the "batteries-included" philosophy, providing many built-in features for common web development tasks.
- 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
# 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 runservertodo_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
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 migrateRegister 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')Django Ninja is a web framework for building APIs with Django and Python 3.6+ type hints.
# 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),
]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}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 = TrueDjango 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- Set up a Django project with Django Ninja
- Create models for a Todo application
- Implement API endpoints for CRUD operations on tasks
- Add authentication to your API
- Write tests for your API endpoints
- Create a user registration endpoint
- Django Documentation
- Django Ninja Documentation
- Django ORM Cookbook
- JWT Authentication
- Django Testing Documentation
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.