Learning Path: This guide is structured progressively, starting with fundamental concepts and advancing to expert-level topics. Each section builds upon previous knowledge.
📚 Table of Contents
🟢 Part 1: Beginner Level
- What is Django?
- Setting Up Your Development Environment
- Understanding Django Architecture
- Your First Django Project
- Creating Django Apps
- Working with URLs & Views
- Introduction to Templates
- Building Your First Feature
🟡 Part 2: Intermediate Level
- Models & Database Design
- Advanced Template Techniques
- Working with Forms
- The Django Admin Interface
- User Authentication & Authorization
- Managing Static Files & Media
- Advanced ORM Queries
- Pagination & Filtering
🔴 Part 3: Advanced Level
- Class-Based Views
- Custom Middleware
- Signals & Custom Managers
- Building REST APIs
- Testing Strategies
- Performance Optimization
- Security Best Practices
- Deployment & Scaling
🎯 Quick Reference
🟢 Part 1: Beginner Level
Goal: Learn Django fundamentals and build your first web application
1. What is Django? {#introduction}
🎯 What You’ll Learn
- What Django is and why it’s popular
- When to use Django
- Django’s core philosophy
Understanding Django
Django is a high-level Python web framework that helps you build web applications faster and with less code. Think of it as a toolkit that provides pre-built components for common web development tasks.
🤔 Why Choose Django?
Perfect for:
- Blog platforms and content management systems
- E-commerce websites
- Social networks
- Data-driven applications
- REST APIs
- Enterprise applications
Not ideal for:
- Simple static websites (use a static site generator)
- Real-time applications like video chat (consider Django Channels or other frameworks)
- Microservices requiring extreme flexibility (though Django can work)
Key Features Explained
| Feature | What It Means | Example |
|---|---|---|
| Batteries Included | Built-in tools for common tasks | User authentication, admin panel, forms |
| ORM | Write Python instead of SQL | User.objects.filter(age__gt=18) instead of raw SQL |
| Security | Protection by default | CSRF protection, SQL injection prevention |
| Scalable | Grows with your app | Used by Instagram, Spotify, YouTube |
| Fast Development | Less boilerplate code | Admin interface auto-generated |
Django’s Philosophy (What Makes it Different)
# ❌ Bad: Repeating code (Violates DRY)
def get_published_posts():
return Post.objects.filter(status='published')
def get_published_count():
return Post.objects.filter(status='published').count()
# ✅ Good: Don't Repeat Yourself (DRY)
class PostManager(models.Manager):
def published(self):
return self.filter(status='published')
# Now use: Post.objects.published() everywherePythonCore Principles:
- DRY (Don’t Repeat Yourself): Write code once, reuse everywhere
- Explicit over Implicit: Code should be clear and obvious
- Loose Coupling: Components work independently
- Convention over Configuration: Sensible defaults reduce setup
🎓 Real-World Analogy
Think of Django as a pre-furnished apartment vs building a house from scratch:
- Furniture (admin panel, auth system) already there
- Utilities (ORM, template engine) connected
- Security (locks, alarms) pre-installed
- You just move in and customize to your needs
What You’ll Build in This Guide
Beginner → A blog with posts, comments, and categories
Intermediate → Add user accounts, media uploads, search
Advanced → REST API, advanced features, deploymentPython2. Setting Up Your Development Environment {#installation}
🎯 What You’ll Learn
- Installing Python and Django
- Setting up a virtual environment
- Verifying your installation
- Installing essential tools
Step-by-Step Installation
Prerequisites Checklist
- Python 3.8+ installed on your computer
- pip (comes with Python)
- A code editor (VS Code, PyCharm, or Sublime Text)
- Basic Python knowledge (variables, functions, classes)
Installation Steps
Step 1: Verify Python Installation
# Check if Python is installed
python --version # Should show 3.8 or higher
# Check if pip is available
pip --versionBash💡 Troubleshooting: If
pythondoesn’t work, trypython3. On Windows, you might need to usepy.
Step 2: Create a Project Directory
# Create and navigate to your project folder
mkdir django_projects
cd django_projectsBashStep 3: Set Up Virtual Environment
Virtual environments keep your project dependencies isolated.
# Create virtual environment
python -m venv blog_env
# Activate it
# On Windows:
blog_env\Scripts\activate
# On macOS/Linux:
source blog_env/bin/activate
# You should see (blog_env) in your terminal promptBash⚠️ Important: Always activate your virtual environment before working on your project!
Step 4: Install Django
# Install Django (latest version)
pip install django
# Or install a specific version
pip install django==4.2
# Verify installation
django-admin --version
# Should output something like: 4.2.7BashStep 5: Install Essential Tools (Optional but Recommended)
Creating Your Project
Step 1: Create the Project
# Make sure your virtual environment is activated!
# You should see (blog_env) in your terminal
# Create project named 'myblog'
django-admin startproject myblog
# Navigate into it
cd myblogBashStep 2: Explore the Structure
myblog/ ← Project root directory
│
├── manage.py ← Command-line tool (YOU'LL USE THIS A LOT!)
│
└── myblog/ ← Project package (inner folder)
├── __init__.py ← Makes this a Python package
├── settings.py ← ⚙️ All configuration (IMPORTANT!)
├── urls.py ← 🗺️ URL routing (IMPORTANT!)
├── asgi.py ← For async deployment (ignore for now)
└── wsgi.py ← For deployment (ignore for now)BashUnderstanding Key Files
1. manage.py – Your Command Center
# You'll use this for everything:
python manage.py runserver # Start development server
python manage.py makemigrations # Create database changes
python manage.py migrate # Apply database changes
python manage.py createsuperuser # Create admin user
# ... and many more commandsBashUnderstanding Apps
Remember:
- Project = Your entire website
- App = One specific feature
E-commerce Project
├── products/ (app - manage products)
├── cart/ (app - shopping cart)
├── orders/ (app - order processing)
└── accounts/ (app - user management)
Blog Project
├── blog/ (app - posts and articles)
├── comments/ (app - comment system)
└── users/ (app - user profiles)BashCreating Your First App
Step 1: Create the ‘blog’ App
# From myblog/ directory (where manage.py is)
python manage.py startapp blogBashStep 2: Explore the App Structure
blog/ ← Your new app!
│
├── migrations/ ← Database version control
│ └── __init__.py
│
├── __init__.py ← Makes this a Python package
├── admin.py ← 🎛️ Register models for admin
├── apps.py ← App configuration
├── models.py ← 📦 Database models (IMPORTANT!)
├── tests.py ← Unit tests
└── views.py ← 🎮 View functions (IMPORTANT!)BashUnderstanding App Files
| File | Purpose | You’ll Use It For |
|---|---|---|
models.py | Define database structure | Creating Post, Comment, Category models |
views.py | Write view logic | Handling requests, rendering pages |
admin.py | Customize admin panel | Making models editable in admin |
tests.py | Write tests | Testing your code (important!) |
apps.py | App configuration | Usually don’t touch |
migrations/ | Database change history | Auto-generated, don’t edit |
Registering Your App
Django needs to know about your app!
Edit myblog/settings.py:
INSTALLED_APPS = [
# Built-in Django apps
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
# Your apps
'blog', # ← Add this line!
]Python💡 Pro Tip: Add your apps at the end. Built-in apps should stay at the top.
Complete Project Structure Now
myblog/ ← Project root
│
├── blog/ ← Your blog app
│ ├── migrations/
│ ├── __init__.py
│ ├── admin.py
│ ├── apps.py
│ ├── models.py
│ ├── tests.py
│ └── views.py
│
├── myblog/ ← Project settings
│ ├── __init__.py
│ ├── settings.py
│ ├── urls.py
│ ├── asgi.py
│ └── wsgi.py
│
├── db.sqlite3 ← Database file
└── manage.py ← Management commandsBashQuick Test
Let’s verify the app is recognized:
python manage.py checkBashYou should see:
System check identified no issues (0 silenced).Bash✅ Success! Your app is ready.
🎓 Quick Recap
✅ Created a ‘blog’ app using startapp
✅ Understood the app directory structure
✅ Registered the app in settings.py
✅ Verified with python manage.py check
Next: Let’s create views and URLs to display pages!
6. Working with URLs & Views {#urls-views}
🎯 What You’ll Learn
- Creating your first view
- Setting up URL patterns
- Rendering responses
- Understanding the request/response cycle
The View-URL Connection
User visits URL → Django matches URL → Calls View → Returns ResponseBashYour First View
Step 1: Create a Simple View
Edit blog/views.py:
from django.http import HttpResponse
def home(request):
"""
Every view receives a request object
Every view must return a response
"""
return HttpResponse("<h1>Welcome to My Blog!</h1>")PythonLet’s break it down:
def home(request):
# 'request' contains info about the user's request
# - request.method: 'GET', 'POST', etc.
# - request.user: Current user
# - request.GET: Query parameters
# HttpResponse sends HTML to the browser
return HttpResponse("<h1>Welcome to My Blog!</h1>")PythonStep 2: Create App URLs
Create a new file blog/urls.py:
from django.urls import path
from . import views # Import views from current app
# App-specific URLs
urlpatterns = [
path('', views.home, name='home'), # blog/ → home view
]PythonUnderstanding URL Patterns:
path('', views.home, name='home')
# │ │ │
# │ │ └─ Name (for reverse lookups)
# │ └─────────── Which view to call
# └─────────────────── URL pathPythonStep 3: Connect to Main URLs
Edit myblog/urls.py:
from django.contrib import admin
from django.urls import path, include # ← Add 'include'
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('blog.urls')), # ← Add this!
]PythonWhat this does:
http://localhost:8000/ → blog.urls → home view
http://localhost:8000/admin/ → admin sitePythonStep 4: Test It!
# Start server
python manage.py runserver
# Visit: http://127.0.0.1:8000/PythonYou should see: Welcome to My Blog!
Creating Multiple Views
Add more views to blog/views.py:
from django.http import HttpResponse
def home(request):
return HttpResponse("<h1>Welcome to My Blog!</h1>")
def about(request):
return HttpResponse("<h1>About Me</h1><p>This is my blog about Django!</p>")
def contact(request):
return HttpResponse("<h1>Contact</h1><p>Email: myblog@example.com</p>")PythonUpdate blog/urls.py:
from django.urls import path
from . import views
urlpatterns = [
path('', views.home, name='home'),
path('about/', views.about, name='about'),
path('contact/', views.contact, name='contact'),
]PythonTest the URLs:
http://127.0.0.1:8000/ → Home page
http://127.0.0.1:8000/about/ → About page
http://127.0.0.1:8000/contact/ → Contact pagePythonURL Patterns with Parameters
Dynamic URLs with parameters:
# blog/views.py
def post_detail(request, post_id):
return HttpResponse(f"<h1>Viewing Post #{post_id}</h1>")
# blog/urls.py
urlpatterns = [
path('', views.home, name='home'),
path('post/<int:post_id>/', views.post_detail, name='post_detail'),
# └── URL parameter (must be an integer)
]PythonTry it:
http://127.0.0.1:8000/post/1/ → Viewing Post #1
http://127.0.0.1:8000/post/42/ → Viewing Post #42PythonDifferent parameter types:
path('post/<int:id>/', ...) # Integer: 1, 2, 3
path('post/<slug:slug>/', ...) # Slug: my-first-post
path('post/<str:title>/', ...) # String: anything
path('year/<int:year>/', ...) # Year: 2024PythonURL Naming and Reverse Lookup
Why name URLs?
# ❌ Bad: Hardcoded URL
return HttpResponse('<a href="/about/">About</a>')
# ✅ Good: Named URL (flexible!)
from django.urls import reverse
url = reverse('about') # Returns '/about/'PythonUsing names in templates (you’ll learn this soon):
<a href="{% url 'home' %}">Home</a>
<a href="{% url 'about' %}">About</a>
<a href="{% url 'post_detail' 123 %}">View Post 123</a>PythonRequest and Response Objects
Understanding the request object:
def my_view(request):
# HTTP method
method = request.method # 'GET', 'POST', etc.
# Query parameters (?search=django)
search = request.GET.get('search', '')
# Current user
user = request.user
# Request path
path = request.path # '/about/'
return HttpResponse(f"Method: {method}, Search: {search}")PythonDifferent response types:
from django.http import HttpResponse, JsonResponse, HttpResponseRedirect
# HTML response
return HttpResponse("<h1>Hello</h1>")
# JSON response
return JsonResponse({'message': 'Hello', 'status': 'success'})
# Redirect
return HttpResponseRedirect('/home/')Python🎓 Practice Challenge
Create these views and URLs:
- Blog list page at
/posts/showing “All Blog Posts” - Archive page at
/archive/2024/showing the year - Search page at
/search/showing a search term from?q=django
💡 Click to see solution
# blog/views.py
def post_list(request):
return HttpResponse("<h1>All Blog Posts</h1>")
def archive(request, year):
return HttpResponse(f"<h1>Posts from {year}</h1>")
def search(request):
query = request.GET.get('q', 'No query')
return HttpResponse(f"<h1>Search Results for: {query}</h1>")
# blog/urls.py
urlpatterns = [
path('posts/', views.post_list, name='post_list'),
path('archive/<int:year>/', views.archive, name='archive'),
path('search/', views.search, name='search'),
]Python🎓 Key Takeaways
✅ Views are Python functions that handle requests
✅ Every view receives a request and returns a response
✅ URL patterns map URLs to views
✅ Use <type:name> for URL parameters
✅ Name your URLs for easy reference
✅ include() organizes URLs per app
Next: Let’s make our pages look good with templates!
7. Introduction to Templates {#templates-basic}
🎯 What You’ll Learn
- What templates are
- Creating HTML templates
- Template syntax basics
- Rendering templates in views
- Passing data to templates path(‘admin/’, admin.site.urls), # Admin panel at /admin/ Your URLs will go here ]
Running Your First Server
Step 1: Start the Server
# From myblog/ directory (where manage.py is)
python manage.py runserverPythonYou’ll see:
Watching for file changes with StatReloader
Performing system checks...
System check identified no issues (0 silenced).
You have 18 unapplied migration(s)...
Run 'python manage.py migrate' to apply them.
November 30, 2025 - 12:00:00
Django version 4.2.7, using settings 'myblog.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CTRL-BREAK.PythonStep 2: View Your Site
- Open browser
- Go to:
http://127.0.0.1:8000/orhttp://localhost:8000/ - You should see a rocket ship! 🚀
![Django Welcome Page – “The install worked successfully! Congratulations!”]
Step 3: Access Admin Panel
Try: http://127.0.0.1:8000/admin/ (you’ll get an error – that’s OK! We’ll fix this soon)
Common Server Commands
# Default (runs on http://127.0.0.1:8000/)
python manage.py runserver
# Custom port
python manage.py runserver 8080
# Access at: http://127.0.0.1:8080/
# Accessible from other devices on network
python manage.py runserver 0.0.0.0:8000
# Stop server: Press CTRL+CPythonMaking Your First Change
Let’s customize the timezone and language!
Edit myblog/settings.py:
# Find these lines and change them:
LANGUAGE_CODE = 'en-us' # Change to your language
# Examples: 'es' (Spanish), 'fr' (French), 'de' (German)
TIME_ZONE = 'America/New_York' # Change to your timezone
# Examples: 'Europe/London', 'Asia/Tokyo', 'Australia/Sydney'
# Full list: https://en.wikipedia.org/wiki/List_of_tz_database_time_zonesPythonSave and refresh your browser – changes apply automatically! (server auto-reloads)
Initial Database Setup
# Apply built-in migrations
python manage.py migratePythonYou’ll see:
Operations to perform:
Apply all migrations: admin, auth, contenttypes, sessions
Running migrations:
Applying contenttypes.0001_initial... OK
Applying auth.0001_initial... OK
...PythonThis creates a db.sqlite3 file (your database).
🎓 What You Just Did
✅ Created a Django project
✅ Understood the project structure
✅ Ran the development server
✅ Made configuration changes
✅ Set up the initial database
Next: Let’s create your first app!
5. Creating Django Apps {#apps}
🎯 What You’ll Learn
- Difference between projects and apps
- Creating your first app
- Registering apps
- App structure explaineded
- Request/response flow
- Difference between project and app
The MTV Pattern (Model-Template-View)
Django uses MTV, which is similar to the popular MVC pattern but with different naming:
Traditional MVC Django MTV
├── Model ├── Model (Data & Business Logic)
├── View ├── Template (Presentation/HTML)
└── Controller └── View (Logic/Controller)PythonVisual Breakdown
┌─────────────────────────────────────────────────────┐
│ User Types: www.myblog.com/posts/ │
└────────────────────┬────────────────────────────────┘
│
▼
┌────────────────────────────────────────────────────┐
│ 1. URL Dispatcher (urls.py) │
│ "Which view should handle this?" │
│ /posts/ → post_list view │
└────────────────┬───────────────────────────────────┘
│
▼
┌────────────────────────────────────────────────────┐
│ 2. View (views.py) │
│ "Get the data and decide what to show" │
│ - Fetch posts from database │
│ - Process business logic │
└────────────────┬───────────────────────────────────┘
│
▼
┌────────────────────────────────────────────────────┐
│ 3. Model (models.py) │
│ "Interact with database" │
│ Post.objects.all() → Returns all posts │
└────────────────┬───────────────────────────────────┘
│
▼
┌────────────────────────────────────────────────────┐
│ 4. Template (template.html) │
│ "Display the data as HTML" │
│ <h1>{{ post.title }}</h1> │
└────────────────┬───────────────────────────────────┘
│
▼
┌────────────────────────────────────────────────────┐
│ User Sees: Beautiful blog page with posts │
└────────────────────────────────────────────────────┘PythonComponent Deep Dive
📦 Model (M) – Your Data Structure
What it does: Defines how data is stored and retrieved
# models.py - Defines a blog post
class Post(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
# This is your database table structure!PythonThink of it as: A spreadsheet blueprint
- title column: holds text (max 200 chars)
- content column: holds long text
- created_at column: holds date/time
📄 Template (T) – Your HTML
What it does: Controls how data appears to users
<!-- post_list.html -->
<h1>My Blog</h1>
{% for post in posts %}
<article>
<h2>{{ post.title }}</h2>
<p>{{ post.content }}</p>
<small>{{ post.created_at }}</small>
</article>
{% endfor %}PythonThink of it as: The visual design/layout
🎮 View (V) – Your Logic Controller
What it does: Connects models and templates
# views.py
def post_list(request):
posts = Post.objects.all() # Get data from Model
return render(request, 'post_list.html', {'posts': posts})
# Send data to TemplatePythonThink of it as: The middleman coordinating everything
Project vs App: What’s the Difference?
Project (myblog) App (blog)
├── The entire website ├── One feature/module
├── Contains settings ├── Posts functionality
├── Main URL routing ├── Has own models, views
└── Can have many apps └── Reusable in other projects
Example:
myblog_project/
├── blog/ (app for posts)
├── accounts/ (app for users)
└── comments/ (app for comments)PythonReal-world analogy:
- Project = A shopping mall
- Apps = Individual stores (electronics, clothing, food court)
Complete Request Flow Example
# 1. URL (urls.py)
path('posts/', views.post_list, name='post_list')
# 2. View (views.py)
def post_list(request):
posts = Post.objects.filter(published=True) # 3. Model
return render(request, 'posts.html', {'posts': posts}) # 4. TemplatePython<!-- 4. Template (posts.html) -->
{% for post in posts %}
<h2>{{ post.title }}</h2>
{% endfor %}Python🎓 Key Takeaways
✅ Model = Database structure (what data to store)
✅ Template = HTML presentation (how to display)
✅ View = Business logic (what to show and when)
✅ URL = Routes traffic to the right view
✅ Project = Your entire website
✅ App = A specific feature/module
4. Your First Django Project {#first-project}
🎯 What You’ll Learn
- Creating a Django project
- Understanding the project structure
- Running the development server
- Making your first change
Django follows the MTV (Model-Template-View) pattern, similar to MVC:
Model (M):
- Defines data structure
- Database schema
- Business logic
- ORM for database operations
Template (T):
- Presentation layer
- HTML with Django template language
- Renders dynamic content
View (V):
- Business logic
- Processes requests
- Returns responses
- Interacts with models and templates
URL Dispatcher:
- Maps URLs to views
- Routes incoming requests
Flow: URL → View → Model → Template → Response
4. Creating Your First Django Project {#first-project}
Start a New Project:
# Create a new Django project
django-admin startproject myproject
# Project structure:
myproject/
manage.py
myproject/
__init__.py
settings.py
urls.py
asgi.py
wsgi.pyPythonKey Files:
- manage.py: Command-line utility for project management
- settings.py: Configuration settings
- urls.py: URL declarations
- wsgi.py: Web Server Gateway Interface entry point
- asgi.py: Asynchronous Server Gateway Interface entry point
Run Development Server:
cd myproject
python manage.py runserver
# Run on specific port
python manage.py runserver 8080
# Run on specific IP and port
python manage.py runserver 0.0.0.0:8000PythonVisit http://127.0.0.1:8000/ to see the welcome page.
5. Django Apps {#apps}
Apps are modular components that perform specific functions. A project can have multiple apps.
Create an App:
python manage.py startapp blog
# App structure:
blog/
__init__.py
admin.py
apps.py
models.py
tests.py
views.py
migrations/
__init__.pyPythonRegister the App:
In myproject/settings.py:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'blog', # Add your app
]Python6. Models & Databases {#models}
What Are Models?
Models define your database structure using Python classes instead of SQL.
# Instead of writing SQL:
CREATE TABLE blog_post (
id INTEGER PRIMARY KEY,
title VARCHAR(200),
content TEXT
);
# You write Python:
class Post(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()PythonCreating Your Blog Models
Edit blog/models.py:
from django.db import models
from django.contrib.auth.models import User
from django.utils import timezone
class Category(models.Model):
"""Categories for organizing posts"""
name = models.CharField(max_length=100)
slug = models.SlugField(unique=True)
description = models.TextField(blank=True)
class Meta:
verbose_name_plural = "Categories" # Plural form in admin
ordering = ['name'] # Order alphabetically
def __str__(self):
return self.name # How it appears in admin
class Post(models.Model):
"""Main blog post model"""
STATUS_CHOICES = [
('draft', 'Draft'),
('published', 'Published'),
]
# Basic fields
title = models.CharField(max_length=200)
slug = models.SlugField(unique=True, max_length=200)
content = models.TextField()
excerpt = models.TextField(max_length=300, blank=True)
# Relationships
author = models.ForeignKey(
User,
on_delete=models.CASCADE, # Delete posts when user deleted
related_name='posts' # Access via user.posts.all()
)
category = models.ForeignKey(
Category,
on_delete=models.SET_NULL, # Keep post when category deleted
null=True,
related_name='posts'
)
# Dates
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
published_at = models.DateTimeField(default=timezone.now)
# Other fields
status = models.CharField(
max_length=10,
choices=STATUS_CHOICES,
default='draft'
)
featured_image = models.ImageField(
upload_to='posts/%Y/%m/%d/', # Organize by date
blank=True
)
views = models.PositiveIntegerField(default=0)
class Meta:
ordering = ['-published_at'] # Newest first
indexes = [
models.Index(fields=['-published_at']), # Speed up queries
]
def __str__(self):
return self.title
class Comment(models.Model):
"""Comments on blog posts"""
post = models.ForeignKey(
Post,
on_delete=models.CASCADE, # Delete comments with post
related_name='comments'
)
name = models.CharField(max_length=100)
email = models.EmailField()
content = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
active = models.BooleanField(default=True) # For moderation
class Meta:
ordering = ['created_at'] # Oldest first
def __str__(self):
return f'Comment by {self.name} on {self.post}'PythonUnderstanding Field Types:
CharField: Short textTextField: Long textIntegerField: IntegersDateField/DateTimeField: DatesEmailField: Email validationURLField: URL validationBooleanField: True/FalseFileField/ImageField: File uploadsForeignKey: One-to-many relationshipManyToManyField: Many-to-many relationshipOneToOneField: One-to-one relationship
Field Options:
null=True: Allow NULL in databaseblank=True: Allow empty in formsdefault: Default valueunique=True: Unique constraintchoices: Limited optionsmax_length: Maximum length
Make Migrations:
# Create migration files
python manage.py makemigrations
# Apply migrations
python manage.py migrate
# View migration SQL
python manage.py sqlmigrate blog 0001
# Show migrations
python manage.py showmigrationsPython7. Views {#views}
Views handle the logic of your application and return responses.
Function-Based Views (FBV):
In blog/views.py:
from django.shortcuts import render, get_object_or_404, redirect
from django.http import HttpResponse, JsonResponse, Http404
from django.core.paginator import Paginator
from django.db.models import Q
from .models import Post, Category, Comment
from .forms import CommentForm
def post_list(request):
"""Display list of published posts"""
posts = Post.objects.filter(status='published').select_related('author', 'category')
# Pagination
paginator = Paginator(posts, 10) # 10 posts per page
page_number = request.GET.get('page')
page_obj = paginator.get_page(page_number)
context = {
'page_obj': page_obj,
'posts': page_obj,
}
return render(request, 'blog/post_list.html', context)
def post_detail(request, slug):
"""Display single post"""
post = get_object_or_404(Post, slug=slug, status='published')
# Increment views
post.views += 1
post.save(update_fields=['views'])
# Get comments
comments = post.comments.filter(active=True)
# Handle comment form
if request.method == 'POST':
form = CommentForm(request.POST)
if form.is_valid():
comment = form.save(commit=False)
comment.post = post
comment.save()
return redirect('post_detail', slug=post.slug)
else:
form = CommentForm()
context = {
'post': post,
'comments': comments,
'form': form,
}
return render(request, 'blog/post_detail.html', context)
def category_posts(request, slug):
"""Display posts by category"""
category = get_object_or_404(Category, slug=slug)
posts = Post.objects.filter(category=category, status='published')
context = {
'category': category,
'posts': posts,
}
return render(request, 'blog/category_posts.html', context)
def search(request):
"""Search posts"""
query = request.GET.get('q', '')
posts = Post.objects.none()
if query:
posts = Post.objects.filter(
Q(title__icontains=query) |
Q(content__icontains=query),
status='published'
)
context = {
'posts': posts,
'query': query,
}
return render(request, 'blog/search.html', context)PythonCommon Response Types:
from django.http import HttpResponse, JsonResponse, HttpResponseRedirect
from django.shortcuts import render, redirect
# HTML response
return render(request, 'template.html', context)
# Plain text
return HttpResponse("Hello, World!")
# JSON response
return JsonResponse({'key': 'value'})
# Redirect
return redirect('view_name')
return redirect('/path/to/page/')Python8. Templates {#templates}
Templates render HTML with dynamic content using Django Template Language (DTL).
Template Structure:
Create blog/templates/blog/ directory:
blog/
templates/
blog/
base.html
post_list.html
post_detail.htmlPythonBase Template (base.html):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}My Blog{% endblock %}</title>
{% load static %}
<link rel="stylesheet" href="{% static 'css/style.css' %}">
</head>
<body>
<header>
<nav>
<a href="{% url 'post_list' %}">Home</a>
<a href="{% url 'about' %}">About</a>
<form method="get" action="{% url 'search' %}">
<input type="text" name="q" placeholder="Search...">
<button type="submit">Search</button>
</form>
</nav>
</header>
<main>
{% block content %}
{% endblock %}
</main>
<footer>
<p>© {% now "Y" %} My Blog. All rights reserved.</p>
</footer>
{% block extra_js %}
{% endblock %}
</body>
</html>PythonPost List Template (post_list.html):
{% extends 'blog/base.html' %}
{% block title %}Blog Posts{% endblock %}
{% block content %}
<h1>Latest Posts</h1>
{% if posts %}
<div class="post-grid">
{% for post in posts %}
<article class="post-card">
{% if post.featured_image %}
<img src="{{ post.featured_image.url }}" alt="{{ post.title }}">
{% endif %}
<h2><a href="{% url 'post_detail' post.slug %}">{{ post.title }}</a></h2>
<div class="post-meta">
<span>By {{ post.author.username }}</span>
<span>{{ post.published_at|date:"F d, Y" }}</span>
<span>{{ post.views }} views</span>
</div>
<p>{{ post.excerpt|truncatewords:30 }}</p>
{% if post.category %}
<span class="category">{{ post.category.name }}</span>
{% endif %}
</article>
{% endfor %}
</div>
<!-- Pagination -->
{% if page_obj.has_other_pages %}
<div class="pagination">
{% if page_obj.has_previous %}
<a href="?page=1">First</a>
<a href="?page={{ page_obj.previous_page_number }}">Previous</a>
{% endif %}
<span>Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}</span>
{% if page_obj.has_next %}
<a href="?page={{ page_obj.next_page_number }}">Next</a>
<a href="?page={{ page_obj.paginator.num_pages }}">Last</a>
{% endif %}
</div>
{% endif %}
{% else %}
<p>No posts available.</p>
{% endif %}
{% endblock %}PythonPost Detail Template (post_detail.html):
{% extends 'blog/base.html' %}
{% block title %}{{ post.title }}{% endblock %}
{% block content %}
<article class="post-detail">
<h1>{{ post.title }}</h1>
<div class="post-meta">
<span>By {{ post.author.get_full_name|default:post.author.username }}</span>
<span>{{ post.published_at|date:"F d, Y" }}</span>
<span>{{ post.views }} views</span>
</div>
{% if post.featured_image %}
<img src="{{ post.featured_image.url }}" alt="{{ post.title }}">
{% endif %}
<div class="post-content">
{{ post.content|linebreaks }}
</div>
<!-- Comments Section -->
<section class="comments">
<h2>Comments ({{ comments.count }})</h2>
{% for comment in comments %}
<div class="comment">
<strong>{{ comment.name }}</strong>
<span>{{ comment.created_at|timesince }} ago</span>
<p>{{ comment.content }}</p>
</div>
{% empty %}
<p>No comments yet.</p>
{% endfor %}
<!-- Comment Form -->
<h3>Leave a Comment</h3>
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Submit</button>
</form>
</section>
</article>
{% endblock %}PythonTemplate Tags & Filters:
{# Variables #}
{{ variable }}
{{ user.username }}
{{ post.title|upper }}
{# Filters #}
{{ text|lower }}
{{ text|truncatewords:30 }}
{{ date|date:"Y-m-d" }}
{{ value|default:"N/A" }}
{{ html|safe }}
{# Tags #}
{% if condition %}
...
{% elif other_condition %}
...
{% else %}
...
{% endif %}
{% for item in items %}
{{ forloop.counter }}. {{ item }}
{% empty %}
No items
{% endfor %}
{% url 'view_name' arg1 arg2 %}
{% static 'path/to/file.css' %}
{# Comments #}
{# This is a comment #}
{% comment %}
Multi-line comment
{% endcomment %}
{# Include other templates #}
{% include 'partials/header.html' %}
{# Template inheritance #}
{% extends 'base.html' %}
{% block content %}...{% endblock %}Python9. URLs & Routing {#urls}
URLs map web addresses to views.
Project URLs (myproject/urls.py):
from django.contrib import admin
from django.urls import path, include
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('blog.urls')),
]
# Serve media files in development
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)PythonApp URLs (blog/urls.py):
from django.urls import path
from . import views
app_name = 'blog'
urlpatterns = [
path('', views.post_list, name='post_list'),
path('post/<slug:slug>/', views.post_detail, name='post_detail'),
path('category/<slug:slug>/', views.category_posts, name='category_posts'),
path('search/', views.search, name='search'),
]PythonURL Patterns:
from django.urls import path, re_path
# Basic path
path('about/', views.about, name='about'),
# With parameters
path('post/<int:id>/', views.post_detail, name='post_detail'),
path('post/<slug:slug>/', views.post_detail, name='post_detail'),
path('year/<int:year>/', views.year_archive, name='year_archive'),
# Regular expressions
re_path(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive),
# Path converters:
# - str: Matches any non-empty string (excluding '/')
# - int: Matches zero or any positive integer
# - slug: Matches ASCII letters, numbers, hyphens, and underscores
# - uuid: Matches a UUID
# - path: Matches any non-empty string (including '/')Python10. Forms {#forms}
Forms handle user input validation and processing.
Django Forms:
In blog/forms.py:
from django import forms
from django.core.exceptions import ValidationError
from .models import Comment, Post
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ['name', 'email', 'content']
widgets = {
'name': forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'Your Name'}),
'email': forms.EmailInput(attrs={'class': 'form-control', 'placeholder': 'Your Email'}),
'content': forms.Textarea(attrs={'class': 'form-control', 'placeholder': 'Your Comment', 'rows': 4}),
}
labels = {
'name': 'Name',
'email': 'Email Address',
'content': 'Comment',
}
def clean_content(self):
content = self.cleaned_data.get('content')
if len(content) < 10:
raise ValidationError('Comment must be at least 10 characters long.')
return content
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = ['title', 'slug', 'category', 'content', 'excerpt', 'status', 'featured_image']
widgets = {
'content': forms.Textarea(attrs={'rows': 10}),
'excerpt': forms.Textarea(attrs={'rows': 3}),
}
class ContactForm(forms.Form):
name = forms.CharField(max_length=100, required=True)
email = forms.EmailField(required=True)
subject = forms.CharField(max_length=200, required=True)
message = forms.CharField(widget=forms.Textarea, required=True)
def clean_email(self):
email = self.cleaned_data.get('email')
if not email.endswith('@example.com'):
raise ValidationError('Please use your company email.')
return email
def clean(self):
cleaned_data = super().clean()
# Cross-field validation
return cleaned_dataPythonUsing Forms in Views:
from django.shortcuts import render, redirect
from .forms import ContactForm
def contact(request):
if request.method == 'POST':
form = ContactForm(request.POST)
if form.is_valid():
# Process the data
name = form.cleaned_data['name']
email = form.cleaned_data['email']
subject = form.cleaned_data['subject']
message = form.cleaned_data['message']
# Send email or save to database
# send_mail(subject, message, email, ['admin@example.com'])
return redirect('success')
else:
form = ContactForm()
return render(request, 'blog/contact.html', {'form': form})PythonRendering Forms in Templates:
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{# Render entire form #}
{{ form.as_p }}
{# Or render manually #}
{% for field in form %}
<div class="form-group">
{{ field.label_tag }}
{{ field }}
{% if field.errors %}
<div class="error">{{ field.errors }}</div>
{% endif %}
</div>
{% endfor %}
{# Non-field errors #}
{% if form.non_field_errors %}
<div class="error">{{ form.non_field_errors }}</div>
{% endif %}
<button type="submit">Submit</button>
</form>Python11. Admin Interface {#admin}
Django provides a powerful automatic admin interface.
Register Models (blog/admin.py):
from django.contrib import admin
from .models import Post, Category, Comment
@admin.register(Category)
class CategoryAdmin(admin.ModelAdmin):
list_display = ['name', 'slug']
prepopulated_fields = {'slug': ('name',)}
search_fields = ['name']
@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
list_display = ['title', 'author', 'category', 'status', 'published_at', 'views']
list_filter = ['status', 'created_at', 'published_at', 'category']
search_fields = ['title', 'content']
prepopulated_fields = {'slug': ('title',)}
raw_id_fields = ['author']
date_hierarchy = 'published_at'
ordering = ['-published_at']
list_editable = ['status']
list_per_page = 20
fieldsets = (
('Basic Information', {
'fields': ('title', 'slug', 'author', 'category')
}),
('Content', {
'fields': ('content', 'excerpt', 'featured_image')
}),
('Publication', {
'fields': ('status', 'published_at')
}),
)
def get_queryset(self, request):
qs = super().get_queryset(request)
return qs.select_related('author', 'category')
@admin.register(Comment)
class CommentAdmin(admin.ModelAdmin):
list_display = ['name', 'email', 'post', 'created_at', 'active']
list_filter = ['active', 'created_at']
search_fields = ['name', 'email', 'content']
actions = ['approve_comments']
def approve_comments(self, request, queryset):
queryset.update(active=True)
approve_comments.short_description = "Approve selected comments"PythonCreate Superuser:
python manage.py createsuperuserPythonCustomize Admin Site:
# In admin.py
admin.site.site_header = "My Blog Administration"
admin.site.site_title = "My Blog Admin"
admin.site.index_title = "Welcome to My Blog Administration"Python12. Authentication & Authorization {#auth}
Django provides built-in authentication system.
Login/Logout Views:
In blog/views.py:
from django.contrib.auth import login, logout, authenticate
from django.contrib.auth.decorators import login_required
from django.contrib.auth.forms import UserCreationForm, AuthenticationForm
from django.contrib.auth.models import User
def register(request):
if request.method == 'POST':
form = UserCreationForm(request.POST)
if form.is_valid():
user = form.save()
login(request, user)
return redirect('post_list')
else:
form = UserCreationForm()
return render(request, 'registration/register.html', {'form': form})
def user_login(request):
if request.method == 'POST':
form = AuthenticationForm(request, data=request.POST)
if form.is_valid():
username = form.cleaned_data.get('username')
password = form.cleaned_data.get('password')
user = authenticate(username=username, password=password)
if user is not None:
login(request, user)
return redirect('post_list')
else:
form = AuthenticationForm()
return render(request, 'registration/login.html', {'form': form})
def user_logout(request):
logout(request)
return redirect('post_list')
@login_required
def profile(request):
return render(request, 'registration/profile.html')PythonCustom User Model:
In myproject/settings.py:
AUTH_USER_MODEL = 'blog.CustomUser'PythonIn blog/models.py:
from django.contrib.auth.models import AbstractUser
class CustomUser(AbstractUser):
bio = models.TextField(blank=True)
avatar = models.ImageField(upload_to='avatars/', blank=True)
website = models.URLField(blank=True)
def __str__(self):
return self.usernamePythonPermission Decorators:
from django.contrib.auth.decorators import login_required, permission_required, user_passes_test
@login_required
def my_view(request):
pass
@permission_required('blog.add_post')
def create_post(request):
pass
def is_author(user):
return user.groups.filter(name='Authors').exists()
@user_passes_test(is_author)
def author_only_view(request):
passPythonURL Configuration:
from django.contrib.auth import views as auth_views
urlpatterns = [
path('login/', auth_views.LoginView.as_view(), name='login'),
path('logout/', auth_views.LogoutView.as_view(), name='logout'),
path('password_change/', auth_views.PasswordChangeView.as_view(), name='password_change'),
path('password_reset/', auth_views.PasswordResetView.as_view(), name='password_reset'),
]Python13. Static Files & Media {#static}
Settings Configuration:
In myproject/settings.py:
import os
# Static files (CSS, JavaScript, Images)
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
STATICFILES_DIRS = [
os.path.join(BASE_DIR, 'static'),
]
# Media files (User uploads)
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')PythonDirectory Structure:
myproject/
static/
css/
style.css
js/
script.js
images/
logo.png
media/
posts/
2024/01/01/
image.jpgPythonUsing in Templates:
{% load static %}
<link rel="stylesheet" href="{% static 'css/style.css' %}">
<script src="{% static 'js/script.js' %}"></script>
<img src="{% static 'images/logo.png' %}" alt="Logo">
<!-- Media files -->
<img src="{{ post.featured_image.url }}" alt="{{ post.title }}">PythonCollect Static Files:
python manage.py collectstaticPython14. Django ORM (Object-Relational Mapping) {#orm}
The ORM allows you to interact with databases using Python code.
QuerySets:
from blog.models import Post
# Get all objects
posts = Post.objects.all()
# Filter
published_posts = Post.objects.filter(status='published')
recent_posts = Post.objects.filter(published_at__gte='2024-01-01')
# Exclude
draft_posts = Post.objects.exclude(status='published')
# Get single object
post = Post.objects.get(id=1)
post = Post.objects.get(slug='my-post')
# Get or 404
from django.shortcuts import get_object_or_404
post = get_object_or_404(Post, slug='my-post')
# Create
post = Post.objects.create(
title='New Post',
slug='new-post',
author=request.user,
content='Content here'
)
# Update
Post.objects.filter(id=1).update(views=100)
post.title = 'Updated Title'
post.save()
# Delete
post.delete()
Post.objects.filter(status='draft').delete()
# Count
count = Post.objects.count()
# Exists
exists = Post.objects.filter(slug='my-post').exists()
# Order by
posts = Post.objects.order_by('-published_at')
posts = Post.objects.order_by('title', '-created_at')
# Limit
posts = Post.objects.all()[:5] # First 5
posts = Post.objects.all()[5:10] # Offset and limit
# Distinct
authors = Post.objects.values('author').distinct()PythonComplex Queries:
from django.db.models import Q, F, Count, Avg, Sum, Max, Min
from django.db.models.functions import Lower
# Q objects (OR queries)
posts = Post.objects.filter(Q(title__icontains='django') | Q(content__icontains='django'))
# F expressions (field comparison)
posts = Post.objects.filter(views__gt=F('likes') * 2)
# Aggregation
from django.db.models import Count, Avg
stats = Post.objects.aggregate(
total=Count('id'),
avg_views=Avg('views'),
max_views=Max('views')
)
# Annotation
posts = Post.objects.annotate(comment_count=Count('comments'))
# Select related (for ForeignKey)
posts = Post.objects.select_related('author', 'category')
# Prefetch related (for ManyToMany and reverse ForeignKey)
posts = Post.objects.prefetch_related('comments')
# Values and values_list
posts = Post.objects.values('title', 'slug')
titles = Post.objects.values_list('title', flat=True)
# Raw SQL
posts = Post.objects.raw('SELECT * FROM blog_post WHERE status = %s', ['published'])PythonField Lookups:
# Exact match
Post.objects.filter(title__exact='Django')
# Case-insensitive exact
Post.objects.filter(title__iexact='django')
# Contains
Post.objects.filter(title__contains='Django')
Post.objects.filter(title__icontains='django')
# Starts with / Ends with
Post.objects.filter(title__startswith='Django')
Post.objects.filter(title__endswith='Guide')
# Greater than / Less than
Post.objects.filter(views__gt=100)
Post.objects.filter(views__gte=100)
Post.objects.filter(views__lt=1000)
Post.objects.filter(views__lte=1000)
# In
Post.objects.filter(id__in=[1, 2, 3])
# Range
Post.objects.filter(views__range=(100, 1000))
# Date lookups
Post.objects.filter(published_at__year=2024)
Post.objects.filter(published_at__month=12)
Post.objects.filter(published_at__day=25)
# Null
Post.objects.filter(category__isnull=True)
# Relationship lookups
Post.objects.filter(author__username='john')
Post.objects.filter(category__name='Technology')Python15. Middleware {#middleware}
Middleware is a framework of hooks into Django’s request/response processing.
Creating Custom Middleware:
In blog/middleware.py:
import time
from django.utils.deprecation import MiddlewareMixin
class RequestTimingMiddleware(MiddlewareMixin):
def process_request(self, request):
request.start_time = time.time()
def process_response(self, request, response):
if hasattr(request, 'start_time'):
duration = time.time() - request.start_time
response['X-Request-Duration'] = str(duration)
return response
class CustomHeaderMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
# Code executed before the view
response = self.get_response(request)
# Code executed after the view
response['X-Custom-Header'] = 'My Value'
return responsePythonRegister Middleware:
In myproject/settings.py:
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',
'blog.middleware.RequestTimingMiddleware', # Custom middleware
]Python16. Class-Based Views {#cbv}
Class-based views provide an alternative to function-based views.
Generic Views:
In blog/views.py:
from django.views.generic import ListView, DetailView, CreateView, UpdateView, DeleteView
from django.urls import reverse_lazy
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
from .models import Post
from .forms import PostForm
class PostListView(ListView):
model = Post
template_name = 'blog/post_list.html'
context_object_name = 'posts'
paginate_by = 10
def get_queryset(self):
return Post.objects.filter(status='published').select_related('author', 'category')
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['categories'] = Category.objects.all()
return context
class PostDetailView(DetailView):
model = Post
template_name = 'blog/post_detail.html'
context_object_name = 'post'
slug_field = 'slug'
def get_queryset(self):
return Post.objects.filter(status='published')
def get_object(self):
obj = super().get_object()
# Increment views
obj.views += 1
obj.save(update_fields=['views'])
return obj
class PostCreateView(LoginRequiredMixin, CreateView):
model = Post
form_class = PostForm
template_name = 'blog/post_form.html'
success_url = reverse_lazy('post_list')
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
class PostUpdateView(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
model = Post
form_class = PostForm
template_name = 'blog/post_form.html'
def test_func(self):
post = self.get_object()
return self.request.user == post.author
def get_success_url(self):
return reverse_lazy('post_detail', kwargs={'slug': self.object.slug})
class PostDeleteView(LoginRequiredMixin, UserPassesTestMixin, DeleteView):
model = Post
template_name = 'blog/post_confirm_delete.html'
success_url = reverse_lazy('post_list')
def test_func(self):
post = self.get_object()
return self.request.user == post.authorPythonURLs for CBVs:
from django.urls import path
from .views import PostListView, PostDetailView, PostCreateView, PostUpdateView, PostDeleteView
urlpatterns = [
path('', PostListView.as_view(), name='post_list'),
path('post/<slug:slug>/', PostDetailView.as_view(), name='post_detail'),
path('post/new/', PostCreateView.as_view(), name='post_create'),
path('post/<slug:slug>/edit/', PostUpdateView.as_view(), name='post_update'),
path('post/<slug:slug>/delete/', PostDeleteView.as_view(), name='post_delete'),
]Python17. Building REST API Endpoints {#rest}
🎯 What You’ll Learn
- What REST APIs are and why they’re important
- Building API endpoints without frameworks (Django only)
- Using Django REST Framework for advanced APIs
- Different types of API endpoints
- Authentication and permissions
- API documentation
Understanding REST APIs
REST API = A way for applications to communicate using HTTP requests
Mobile App ←→ REST API ←→ Django Backend ←→ Database
Web App ←→ REST API ←→
Third Party ←→ REST API ←→PythonWhy Build APIs?
- Mobile apps can use your backend
- JavaScript frameworks (React, Vue) can fetch data
- Third-party integrations
- Microservices architecture
API Endpoints Explained
An endpoint is a URL that returns data (usually JSON) instead of HTML:
Traditional Web:
GET /posts/ → Returns HTML page
API Endpoint:
GET /api/posts/ → Returns JSON dataPythonMethod 1: Simple JSON API (No Framework)
🎯 Basic JSON Endpoint
Perfect for simple APIs without extra dependencies.
Create a simple API view:
# blog/views.py
from django.http import JsonResponse
from django.views.decorators.http import require_http_methods
from django.views.decorators.csrf import csrf_exempt
from .models import Post, Category
import json
# GET endpoint - List all posts
def api_posts_list(request):
"""
GET /api/posts/
Returns list of all published posts
"""
posts = Post.objects.filter(status='published').values(
'id', 'title', 'slug', 'content', 'published_at', 'views'
)
# Convert QuerySet to list for JSON serialization
posts_list = list(posts)
return JsonResponse({
'success': True,
'count': len(posts_list),
'data': posts_list
}, safe=False)
# GET endpoint - Single post detail
def api_post_detail(request, post_id):
"""
GET /api/posts/1/
Returns single post details
"""
try:
post = Post.objects.get(id=post_id, status='published')
data = {
'id': post.id,
'title': post.title,
'slug': post.slug,
'content': post.content,
'author': post.author.username,
'category': post.category.name if post.category else None,
'published_at': post.published_at,
'views': post.views,
}
return JsonResponse({
'success': True,
'data': data
})
except Post.DoesNotExist:
return JsonResponse({
'success': False,
'error': 'Post not found'
}, status=404)
# POST endpoint - Create new post
@csrf_exempt # Only for testing! Use CSRF protection in production
@require_http_methods(["POST"])
def api_post_create(request):
"""
POST /api/posts/create/
Creates a new post
"""
try:
# Parse JSON data from request body
data = json.loads(request.body)
# Create post
post = Post.objects.create(
title=data.get('title'),
slug=data.get('slug'),
content=data.get('content'),
author=request.user,
status='draft'
)
return JsonResponse({
'success': True,
'message': 'Post created successfully',
'data': {
'id': post.id,
'title': post.title,
'slug': post.slug,
}
}, status=201)
except Exception as e:
return JsonResponse({
'success': False,
'error': str(e)
}, status=400)
# PUT/PATCH endpoint - Update post
@csrf_exempt
@require_http_methods(["PUT", "PATCH"])
def api_post_update(request, post_id):
"""
PUT/PATCH /api/posts/1/update/
Updates existing post
"""
try:
post = Post.objects.get(id=post_id)
data = json.loads(request.body)
# Update fields
if 'title' in data:
post.title = data['title']
if 'content' in data:
post.content = data['content']
if 'status' in data:
post.status = data['status']
post.save()
return JsonResponse({
'success': True,
'message': 'Post updated successfully',
'data': {
'id': post.id,
'title': post.title,
}
})
except Post.DoesNotExist:
return JsonResponse({
'success': False,
'error': 'Post not found'
}, status=404)
# DELETE endpoint - Delete post
@csrf_exempt
@require_http_methods(["DELETE"])
def api_post_delete(request, post_id):
"""
DELETE /api/posts/1/delete/
Deletes a post
"""
try:
post = Post.objects.get(id=post_id)
post.delete()
return JsonResponse({
'success': True,
'message': 'Post deleted successfully'
})
except Post.DoesNotExist:
return JsonResponse({
'success': False,
'error': 'Post not found'
}, status=404)
# Search endpoint
def api_posts_search(request):
"""
GET /api/posts/search/?q=django
Search posts by query
"""
query = request.GET.get('q', '')
if not query:
return JsonResponse({
'success': False,
'error': 'Query parameter "q" is required'
}, status=400)
posts = Post.objects.filter(
title__icontains=query,
status='published'
).values('id', 'title', 'slug', 'excerpt')
return JsonResponse({
'success': True,
'query': query,
'count': len(posts),
'data': list(posts)
})
# Statistics endpoint
def api_stats(request):
"""
GET /api/stats/
Returns blog statistics
"""
from django.db.models import Count, Sum
total_posts = Post.objects.filter(status='published').count()
total_views = Post.objects.aggregate(Sum('views'))['views__sum'] or 0
total_categories = Category.objects.count()
# Posts by category
posts_by_category = Category.objects.annotate(
post_count=Count('posts')
).values('name', 'post_count')
return JsonResponse({
'success': True,
'data': {
'total_posts': total_posts,
'total_views': total_views,
'total_categories': total_categories,
'posts_by_category': list(posts_by_category),
}
})PythonURL Configuration:
# blog/urls.py
from django.urls import path
from . import views
urlpatterns = [
# ... existing URLs ...
# API Endpoints
path('api/posts/', views.api_posts_list, name='api_posts_list'),
path('api/posts/<int:post_id>/', views.api_post_detail, name='api_post_detail'),
path('api/posts/create/', views.api_post_create, name='api_post_create'),
path('api/posts/<int:post_id>/update/', views.api_post_update, name='api_post_update'),
path('api/posts/<int:post_id>/delete/', views.api_post_delete, name='api_post_delete'),
path('api/posts/search/', views.api_posts_search, name='api_posts_search'),
path('api/stats/', views.api_stats, name='api_stats'),
]PythonTesting Your API:
# Using curl
curl http://127.0.0.1:8000/api/posts/
# Using Python requests
import requests
response = requests.get('http://127.0.0.1:8000/api/posts/')
print(response.json())
# Using browser (GET requests only)
http://127.0.0.1:8000/api/posts/PythonExample JSON Response:
{
"success": true,
"count": 3,
"data": [
{
"id": 1,
"title": "First Post",
"slug": "first-post",
"content": "Content here...",
"published_at": "2024-11-30T10:30:00Z",
"views": 42
},
{
"id": 2,
"title": "Second Post",
"slug": "second-post",
"content": "More content...",
"published_at": "2024-11-29T15:20:00Z",
"views": 28
}
]
}PythonMethod 2: Django REST Framework (Advanced)
🎯 Professional API with DRF
Django REST Framework provides powerful features for building production-ready APIs.
Installation:
pip install djangorestframework
pip install django-filter # For filtering
pip install drf-yasg # For API documentationPythonSettings Configuration:
# myproject/settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
# Third-party apps
'rest_framework',
'django_filters',
'drf_yasg', # Swagger documentation
# Your apps
'blog',
]
# REST Framework Configuration
REST_FRAMEWORK = {
# Default authentication
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.TokenAuthentication',
],
# Default permissions
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAuthenticatedOrReadOnly',
],
# Pagination
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 10,
# Filtering
'DEFAULT_FILTER_BACKENDS': [
'django_filters.rest_framework.DjangoFilterBackend',
'rest_framework.filters.SearchFilter',
'rest_framework.filters.OrderingFilter',
],
# Throttling (rate limiting)
'DEFAULT_THROTTLE_CLASSES': [
'rest_framework.throttling.AnonRateThrottle',
'rest_framework.throttling.UserRateThrottle'
],
'DEFAULT_THROTTLE_RATES': {
'anon': '100/day',
'user': '1000/day'
}
}PythonCreate Serializers:
# blog/serializers.py
from rest_framework import serializers
from .models import Post, Category, Comment
from django.contrib.auth.models import User
class UserSerializer(serializers.ModelSerializer):
"""Serializer for User model"""
class Meta:
model = User
fields = ['id', 'username', 'email', 'first_name', 'last_name']
read_only_fields = ['id']
class CategorySerializer(serializers.ModelSerializer):
"""Serializer for Category model"""
post_count = serializers.SerializerMethodField()
class Meta:
model = Category
fields = ['id', 'name', 'slug', 'description', 'post_count']
read_only_fields = ['id']
def get_post_count(self, obj):
return obj.posts.filter(status='published').count()
class CommentSerializer(serializers.ModelSerializer):
"""Serializer for Comment model"""
class Meta:
model = Comment
fields = ['id', 'post', 'name', 'email', 'content', 'created_at', 'active']
read_only_fields = ['id', 'created_at']
class PostListSerializer(serializers.ModelSerializer):
"""Lightweight serializer for post lists"""
author = serializers.StringRelatedField()
category = serializers.StringRelatedField()
comment_count = serializers.SerializerMethodField()
class Meta:
model = Post
fields = [
'id', 'title', 'slug', 'author', 'category',
'excerpt', 'published_at', 'views', 'comment_count'
]
read_only_fields = ['id', 'published_at', 'views']
def get_comment_count(self, obj):
return obj.comments.filter(active=True).count()
class PostDetailSerializer(serializers.ModelSerializer):
"""Detailed serializer for single post"""
author = UserSerializer(read_only=True)
category = CategorySerializer(read_only=True)
comments = CommentSerializer(many=True, read_only=True)
class Meta:
model = Post
fields = [
'id', 'title', 'slug', 'author', 'category', 'content',
'excerpt', 'created_at', 'updated_at', 'published_at',
'status', 'featured_image', 'views', 'comments'
]
read_only_fields = ['id', 'created_at', 'updated_at', 'views']
def validate_slug(self, value):
"""Custom validation for slug"""
if Post.objects.filter(slug=value).exists():
raise serializers.ValidationError("Post with this slug already exists.")
return value
class PostCreateUpdateSerializer(serializers.ModelSerializer):
"""Serializer for creating/updating posts"""
class Meta:
model = Post
fields = [
'title', 'slug', 'category', 'content', 'excerpt',
'status', 'featured_image'
]
def create(self, validated_data):
# Author is set from request.user in the view
return Post.objects.create(**validated_data)PythonCreate API Views:
# blog/api_views.py
from rest_framework import viewsets, status, filters
from rest_framework.decorators import action, api_view, permission_classes
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated, IsAuthenticatedOrReadOnly, AllowAny
from django_filters.rest_framework import DjangoFilterBackend
from django.db.models import Q
from .models import Post, Category, Comment
from .serializers import (
PostListSerializer, PostDetailSerializer, PostCreateUpdateSerializer,
CategorySerializer, CommentSerializer
)
class PostViewSet(viewsets.ModelViewSet):
"""
ViewSet for Post model
Provides:
- list: GET /api/posts/
- create: POST /api/posts/
- retrieve: GET /api/posts/{id}/
- update: PUT /api/posts/{id}/
- partial_update: PATCH /api/posts/{id}/
- destroy: DELETE /api/posts/{id}/
"""
queryset = Post.objects.filter(status='published')
permission_classes = [IsAuthenticatedOrReadOnly]
filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
filterset_fields = ['category', 'author', 'status']
search_fields = ['title', 'content', 'excerpt']
ordering_fields = ['published_at', 'views', 'created_at']
ordering = ['-published_at']
lookup_field = 'slug'
def get_serializer_class(self):
"""Use different serializers for different actions"""
if self.action == 'list':
return PostListSerializer
elif self.action in ['create', 'update', 'partial_update']:
return PostCreateUpdateSerializer
return PostDetailSerializer
def get_queryset(self):
"""Custom queryset with optimizations"""
queryset = super().get_queryset()
# Optimize queries
queryset = queryset.select_related('author', 'category')
queryset = queryset.prefetch_related('comments')
# Show all posts to staff
if self.request.user.is_staff:
queryset = Post.objects.all()
return queryset
def perform_create(self, serializer):
"""Set author when creating post"""
serializer.save(author=self.request.user)
# Custom action: Increment views
@action(detail=True, methods=['post'], permission_classes=[AllowAny])
def increment_views(self, request, slug=None):
"""
POST /api/posts/{slug}/increment_views/
Increment post view count
"""
post = self.get_object()
post.views += 1
post.save(update_fields=['views'])
return Response({
'success': True,
'views': post.views
})
# Custom action: Get post comments
@action(detail=True, methods=['get'])
def comments(self, request, slug=None):
"""
GET /api/posts/{slug}/comments/
Get all comments for a post
"""
post = self.get_object()
comments = post.comments.filter(active=True)
serializer = CommentSerializer(comments, many=True)
return Response({
'success': True,
'count': comments.count(),
'data': serializer.data
})
# Custom action: Publish post
@action(detail=True, methods=['post'], permission_classes=[IsAuthenticated])
def publish(self, request, slug=None):
"""
POST /api/posts/{slug}/publish/
Publish a draft post
"""
post = self.get_object()
# Only author can publish
if post.author != request.user:
return Response(
{'error': 'You can only publish your own posts'},
status=status.HTTP_403_FORBIDDEN
)
post.status = 'published'
post.save(update_fields=['status'])
return Response({
'success': True,
'message': 'Post published successfully',
'status': post.status
})
class CategoryViewSet(viewsets.ModelViewSet):
"""
ViewSet for Category model
"""
queryset = Category.objects.all()
serializer_class = CategorySerializer
permission_classes = [IsAuthenticatedOrReadOnly]
lookup_field = 'slug'
@action(detail=True, methods=['get'])
def posts(self, request, slug=None):
"""
GET /api/categories/{slug}/posts/
Get all posts in a category
"""
category = self.get_object()
posts = category.posts.filter(status='published')
serializer = PostListSerializer(posts, many=True)
return Response({
'success': True,
'category': category.name,
'count': posts.count(),
'data': serializer.data
})
class CommentViewSet(viewsets.ModelViewSet):
"""
ViewSet for Comment model
"""
queryset = Comment.objects.filter(active=True)
serializer_class = CommentSerializer
permission_classes = [IsAuthenticatedOrReadOnly]
filter_backends = [DjangoFilterBackend, filters.OrderingFilter]
filterset_fields = ['post', 'active']
ordering = ['-created_at']
# Function-based API views
@api_view(['GET'])
@permission_classes([AllowAny])
def api_stats(request):
"""
GET /api/stats/
Get blog statistics
"""
from django.db.models import Count, Sum, Avg
stats = {
'total_posts': Post.objects.filter(status='published').count(),
'total_categories': Category.objects.count(),
'total_comments': Comment.objects.filter(active=True).count(),
'total_views': Post.objects.aggregate(Sum('views'))['views__sum'] or 0,
'avg_views_per_post': Post.objects.aggregate(Avg('views'))['views__avg'] or 0,
'posts_by_category': list(
Category.objects.annotate(
post_count=Count('posts')
).values('name', 'post_count')
)
}
return Response({
'success': True,
'data': stats
})
@api_view(['GET'])
@permission_classes([AllowAny])
def api_search(request):
"""
GET /api/search/?q=django&type=posts
Global search endpoint
"""
query = request.GET.get('q', '')
search_type = request.GET.get('type', 'all')
if not query:
return Response(
{'error': 'Query parameter "q" is required'},
status=status.HTTP_400_BAD_REQUEST
)
results = {}
if search_type in ['all', 'posts']:
posts = Post.objects.filter(
Q(title__icontains=query) | Q(content__icontains=query),
status='published'
)
results['posts'] = PostListSerializer(posts, many=True).data
if search_type in ['all', 'categories']:
categories = Category.objects.filter(
Q(name__icontains=query) | Q(description__icontains=query)
)
results['categories'] = CategorySerializer(categories, many=True).data
return Response({
'success': True,
'query': query,
'results': results
})PythonURL Configuration with Router:
# blog/urls.py
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from . import views, api_views
# DRF Router for ViewSets
router = DefaultRouter()
router.register(r'posts', api_views.PostViewSet, basename='post')
router.register(r'categories', api_views.CategoryViewSet, basename='category')
router.register(r'comments', api_views.CommentViewSet, basename='comment')
urlpatterns = [
# ... existing URLs ...
# API endpoints
path('api/', include(router.urls)),
path('api/stats/', api_views.api_stats, name='api_stats'),
path('api/search/', api_views.api_search, name='api_search'),
# API authentication
path('api-auth/', include('rest_framework.urls')),
]PythonAPI Documentation with Swagger:
# myproject/urls.py
from django.contrib import admin
from django.urls import path, include
from rest_framework import permissions
from drf_yasg.views import get_schema_view
from drf_yasg import openapi
# Swagger documentation
schema_view = get_schema_view(
openapi.Info(
title="Blog API",
default_version='v1',
description="REST API for Blog application",
terms_of_service="https://www.example.com/terms/",
contact=openapi.Contact(email="contact@blog.local"),
license=openapi.License(name="BSD License"),
),
public=True,
permission_classes=(permissions.AllowAny,),
)
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('blog.urls')),
# API documentation
path('swagger/', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'),
path('redoc/', schema_view.with_ui('redoc', cache_timeout=0), name='schema-redoc'),
]PythonAvailable Endpoints:
# Posts
GET /api/posts/ - List all posts
POST /api/posts/ - Create new post
GET /api/posts/{slug}/ - Get single post
PUT /api/posts/{slug}/ - Update post
PATCH /api/posts/{slug}/ - Partial update
DELETE /api/posts/{slug}/ - Delete post
POST /api/posts/{slug}/increment_views/ - Increment views
GET /api/posts/{slug}/comments/ - Get post comments
POST /api/posts/{slug}/publish/ - Publish post
# Categories
GET /api/categories/ - List all categories
POST /api/categories/ - Create category
GET /api/categories/{slug}/ - Get single category
GET /api/categories/{slug}/posts/ - Get category posts
# Comments
GET /api/comments/ - List all comments
POST /api/comments/ - Create comment
# Utilities
GET /api/stats/ - Get statistics
GET /api/search/?q=django - Search
# Documentation
GET /swagger/ - Swagger UI
GET /redoc/ - ReDoc UIPythonTesting the API:
# test_api.py
import requests
BASE_URL = 'http://127.0.0.1:8000/api'
# Get all posts
response = requests.get(f'{BASE_URL}/posts/')
print(response.json())
# Get single post
response = requests.get(f'{BASE_URL}/posts/my-first-post/')
print(response.json())
# Create post (requires authentication)
response = requests.post(
f'{BASE_URL}/posts/',
json={
'title': 'New Post',
'slug': 'new-post',
'content': 'Post content here',
'status': 'published'
},
headers={'Authorization': 'Token your-token-here'}
)
# Search
response = requests.get(f'{BASE_URL}/search/?q=django')
print(response.json())
# Get stats
response = requests.get(f'{BASE_URL}/stats/')
print(response.json())Python🎓 Key Takeaways
✅ Simple JSON endpoints with JsonResponse for basic APIs
✅ Django REST Framework for production-ready APIs
✅ Serializers convert models to JSON
✅ ViewSets provide CRUD operations automatically
✅ Use @action decorator for custom endpoints
✅ Router auto-generates URL patterns
✅ Swagger/ReDoc for API documentation
✅ Permissions control who can access what
Next: Let’s add comprehensive testing!
18. Testing {#testing}
Unit Tests:
In blog/tests.py:
from django.test import TestCase, Client
from django.contrib.auth.models import User
from django.urls import reverse
from .models import Post, Category
class PostModelTest(TestCase):
@classmethod
def setUpTestData(cls):
# Set up non-modified objects used by all test methods
user = User.objects.create_user(username='testuser', password='12345')
Category.objects.create(name='Tech', slug='tech')
Post.objects.create(
title='Test Post',
slug='test-post',
author=user,
content='Test content',
status='published'
)
def test_post_content(self):
post = Post.objects.get(id=1)
self.assertEqual(post.title, 'Test Post')
self.assertEqual(post.slug, 'test-post')
def test_post_str(self):
post = Post.objects.get(id=1)
self.assertEqual(str(post), 'Test Post')
def test_post_absolute_url(self):
post = Post.objects.get(id=1)
# Assuming you have get_absolute_url method
# self.assertEqual(post.get_absolute_url(), '/post/test-post/')
class PostViewTest(TestCase):
def setUp(self):
self.client = Client()
self.user = User.objects.create_user(username='testuser', password='12345')
self.post = Post.objects.create(
title='Test Post',
slug='test-post',
author=self.user,
content='Test content',
status='published'
)
def test_post_list_view(self):
response = self.client.get(reverse('post_list'))
self.assertEqual(response.status_code, 200)
self.assertContains(response, 'Test Post')
self.assertTemplateUsed(response, 'blog/post_list.html')
def test_post_detail_view(self):
response = self.client.get(reverse('post_detail', args=['test-post']))
self.assertEqual(response.status_code, 200)
self.assertContains(response, 'Test Post')
def test_post_create_view_authenticated(self):
self.client.login(username='testuser', password='12345')
response = self.client.post(reverse('post_create'), {
'title': 'New Post',
'slug': 'new-post',
'content': 'New content',
'status': 'draft'
})
self.assertEqual(Post.objects.count(), 2)PythonRun Tests:
# Run all tests
python manage.py test
# Run specific app tests
python manage.py test blog
# Run specific test class
python manage.py test blog.tests.PostModelTest
# Run with verbosity
python manage.py test --verbosity=2
# Keep test database
python manage.py test --keepdbPython19. Deployment {#deployment}
Preparation:
- Update settings for production:
# settings.py
DEBUG = False
ALLOWED_HOSTS = ['yourdomain.com', 'www.yourdomain.com']
# Security settings
SECURE_SSL_REDIRECT = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
SECURE_BROWSER_XSS_FILTER = True
SECURE_CONTENT_TYPE_NOSNIFF = True
X_FRAME_OPTIONS = 'DENY'
# Database (use environment variables)
import os
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': os.environ.get('DB_NAME'),
'USER': os.environ.get('DB_USER'),
'PASSWORD': os.environ.get('DB_PASSWORD'),
'HOST': os.environ.get('DB_HOST'),
'PORT': '5432',
}
}
# Static and media files
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')Python- Create requirements.txt:
pip freeze > requirements.txtPython- Collect static files:
python manage.py collectstaticPythonDeployment Options:
Heroku:
# Install Heroku CLI and login
heroku login
# Create Heroku app
heroku create myapp
# Install required packages
pip install gunicorn dj-database-url whitenoise
# Create Procfile
echo "web: gunicorn myproject.wsgi" > Procfile
# Create runtime.txt
echo "python-3.11.0" > runtime.txt
# Deploy
git push heroku main
# Run migrations
heroku run python manage.py migrate
# Create superuser
heroku run python manage.py createsuperuserPythonDocker:
# Dockerfile
FROM python:3.11-slim
ENV PYTHONUNBUFFERED=1
ENV PYTHONDONTWRITEBYTECODE=1
WORKDIR /app
COPY requirements.txt /app/
RUN pip install --no-cache-dir -r requirements.txt
COPY . /app/
RUN python manage.py collectstatic --noinput
EXPOSE 8000
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "myproject.wsgi:application"]Python# docker-compose.yml
version: '3.8'
services:
db:
image: postgres:14
volumes:
- postgres_data:/var/lib/postgresql/data
environment:
- POSTGRES_DB=mydb
- POSTGRES_USER=myuser
- POSTGRES_PASSWORD=mypassword
web:
build: .
command: gunicorn myproject.wsgi:application --bind 0.0.0.0:8000
volumes:
- .:/app
ports:
- "8000:8000"
depends_on:
- db
environment:
- DATABASE_URL=postgresql://myuser:mypassword@db:5432/mydb
volumes:
postgres_data:Python20. Best Practices {#best-practices}
Code Organization:
- Use apps for modular code: Each app should have a single responsibility
- Keep views thin: Move business logic to models or separate services
- Use managers for common queries: Create custom model managers
- Separate settings: Use different settings files for dev/staging/production
Security:
- Never commit sensitive data: Use environment variables
- Use HTTPS in production: Set SECURE_SSL_REDIRECT
- Keep Django updated: Regular security patches
- Validate user input: Use forms and serializers
- Use CSRF protection: Always include {% csrf_token %}
- Sanitize output: Use {{ variable }} not {{ variable|safe }}
Performance:
- Use select_related and prefetch_related: Reduce database queries
- Add database indexes: On frequently queried fields
- Use caching: Django’s cache framework
- Optimize queries: Use django-debug-toolbar
- Paginate large querysets: Don’t load everything at once
- Use database-level operations: bulk_create, bulk_update
Database Optimization:
# Use select_related for foreign keys
posts = Post.objects.select_related('author', 'category')
# Use prefetch_related for many-to-many
posts = Post.objects.prefetch_related('comments')
# Bulk operations
Post.objects.bulk_create([
Post(title='Post 1', ...),
Post(title='Post 2', ...),
])
# Only fetch needed fields
posts = Post.objects.only('title', 'slug')
posts = Post.objects.defer('content')
# Database indexes
class Post(models.Model):
title = models.CharField(max_length=200, db_index=True)PythonProject Structure:
myproject/
├── myproject/
│ ├── __init__.py
│ ├── settings/
│ │ ├── __init__.py
│ │ ├── base.py
│ │ ├── development.py
│ │ └── production.py
│ ├── urls.py
│ └── wsgi.py
├── apps/
│ ├── blog/
│ ├── users/
│ └── core/
├── static/
├── media/
├── templates/
├── requirements/
│ ├── base.txt
│ ├── development.txt
│ └── production.txt
├── manage.py
└── README.mdPythonEnvironment Variables:
# Use python-decouple
from decouple import config
SECRET_KEY = config('SECRET_KEY')
DEBUG = config('DEBUG', default=False, cast=bool)
DATABASE_URL = config('DATABASE_URL')PythonAdditional Resources
Official Documentation:
- Django Documentation: https://docs.djangoproject.com/
- Django REST Framework: https://www.django-rest-framework.org/
- Django Packages: https://djangopackages.org/
Useful Packages:
- django-debug-toolbar: Debugging tool
- django-extensions: Management command extensions
- django-crispy-forms: Better form rendering
- django-filter: Advanced filtering
- celery: Asynchronous task queue
- django-allauth: Authentication & social login
- django-cors-headers: CORS handling
- django-storages: Cloud storage backends
Commands Reference:
# Project management
django-admin startproject myproject
python manage.py startapp myapp
python manage.py runserver
python manage.py shell
# Database
python manage.py makemigrations
python manage.py migrate
python manage.py dbshell
python manage.py dumpdata > backup.json
python manage.py loaddata backup.json
# User management
python manage.py createsuperuser
python manage.py changepassword username
# Static files
python manage.py collectstatic
python manage.py findstatic filename
# Testing
python manage.py test
python manage.py test --keepdb
# Other
python manage.py check
python manage.py showmigrations
python manage.py sqlmigrate app_name migration_namePythonConclusion
Django is a powerful, batteries-included web framework that enables rapid development of secure and scalable web applications. This guide covers the essential concepts from basic setup to advanced features like REST APIs, authentication, and deployment.
Key Takeaways:
- Django follows the MTV (Model-Template-View) pattern
- The ORM provides powerful database abstraction
- Built-in admin interface saves development time
- Security features are built-in by default
- Class-based views offer reusable components
- Django REST Framework makes API development easy
- Testing is integrated into the framework
- Deployment can be done on various platforms
Continue learning by building real projects, reading the official documentation, and exploring the vibrant Django ecosystem!
Discover more from Altgr Blog
Subscribe to get the latest posts sent to your email.
