What is Django?
Django is a high-level Python web framework that enables rapid development of secure and maintainable websites. Created in 2005 at a newspaper company to meet fast-moving newsroom deadlines, Django has grown to power some of the world's busiest websites including Instagram, Pinterest, and Mozilla.
Think of Django as a complete kitchen with all appliances included - you don't need to buy a separate oven, microwave, or refrigerator. Everything you need to build a web application comes bundled together.
Why Django? "Batteries Included" Philosophy
Django's greatest strength is its comprehensive feature set right out of the box:
- Admin Interface: Automatic, production-ready admin panel for managing data
- ORM (Object-Relational Mapping): Work with databases using Python instead of SQL
- Authentication System: User login, permissions, and security built-in
- Form Handling: Powerful form validation and rendering
- Template Engine: Dynamic HTML generation with inheritance
- URL Routing: Clean, elegant URL patterns
- Security Features: CSRF protection, XSS prevention, SQL injection protection
- Scalability: Used by sites serving millions of users
You spend time building your unique features, not reinventing the wheel!
When to Use Django
Django is perfect when you need:
- Rapid Development: MVP, prototypes, or startup projects with tight deadlines
- Content Management: Blogs, news sites, e-commerce platforms
- Data-Driven Apps: Applications with complex database interactions
- Admin Dashboards: When you need a powerful backend interface
- Secure Applications: Banking, healthcare, or any security-critical app
- Scalable Systems: Apps that need to grow from thousands to millions of users
When NOT to use Django: Very simple APIs (use Flask/FastAPI), real-time apps like chat (use FastAPI with WebSockets), or microservices (Django can be too heavy).
MTV Pattern (Model-Template-View)
Django uses MTV architecture, similar to MVC (Model-View-Controller) but with different terminology:
Django MTV Architecture:
┌─────────────┐
│ Browser │
└──────┬──────┘
│ HTTP Request
▼
┌─────────────────────────────┐
│ URLs (urls.py) │ ← Routes requests to views
└──────────┬──────────────────┘
│
▼
┌─────────────────────────────┐
│ VIEW (views.py) │ ← Business logic
│ - Process request │
│ - Query database via Model │
│ - Return response │
└──────┬──────────────────────┘
│ ▲
▼ │
┌──────────────┐ ┌──────────────┐
│ MODEL │ │ TEMPLATE │
│ (models.py) │ │ (.html) │
│ │ │ │
│ - Database │ │ - HTML │
│ - Data │ │ - Dynamic │
│ - Business │ │ content │
│ rules │ │ │
└──────────────┘ └──────────────┘
M = Model → Data and database logic
T = Template → Presentation layer (HTML)
V = View → Business logic (what data to show)
Getting Started: Django Project Setup
# Install Django
pip install django
# Create a new project
django-admin startproject myproject
cd myproject
# Project structure
myproject/
├── manage.py # Command-line utility
└── myproject/
├── __init__.py
├── settings.py # Project settings
├── urls.py # URL routing
├── asgi.py # Async server gateway
└── wsgi.py # Web server gateway
# Create an app (Django projects contain multiple apps)
python manage.py startapp blog
# App structure
blog/
├── __init__.py
├── admin.py # Admin interface config
├── apps.py # App configuration
├── models.py # Database models
├── views.py # View functions/classes
├── urls.py # App-specific URLs (create this)
├── tests.py # Unit tests
└── migrations/ # Database migrations
# Run development server
python manage.py runserver
# Visit http://127.0.0.1:8000/
Models: Defining Your Data
Models define your database structure using Python classes. Django automatically creates database tables from these models.
# blog/models.py
from django.db import models
from django.contrib.auth.models import User
class Category(models.Model):
name = models.CharField(max_length=100)
slug = models.SlugField(unique=True)
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
verbose_name_plural = "Categories"
def __str__(self):
return self.name
class Post(models.Model):
STATUS_CHOICES = [
('draft', 'Draft'),
('published', 'Published'),
]
title = models.CharField(max_length=200)
slug = models.SlugField(unique=True)
author = models.ForeignKey(User, on_delete=models.CASCADE)
category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True)
content = models.TextField()
status = models.CharField(max_length=10, choices=STATUS_CHOICES, default='draft')
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
views = models.IntegerField(default=0)
class Meta:
ordering = ['-created_at'] # Newest first
def __str__(self):
return self.title
# Create migrations (blueprint for database changes)
python manage.py makemigrations
# Apply migrations (create actual tables)
python manage.py migrate
Django ORM: Querying the Database
The ORM lets you interact with databases using Python instead of writing SQL. It's like having a translator between Python and your database.
# CREATE - Adding data
post = Post.objects.create(
title="My First Post",
slug="my-first-post",
author=user,
content="Hello World!",
status='published'
)
# Or create and save separately
post = Post(title="Second Post", slug="second-post")
post.author = user
post.save()
# READ - Retrieving data
# Get all posts
all_posts = Post.objects.all()
# Get published posts only
published = Post.objects.filter(status='published')
# Get single post (raises error if not found)
post = Post.objects.get(slug='my-first-post')
# Get or return None if not found
post = Post.objects.filter(slug='my-first-post').first()
# Exclude certain posts
drafts = Post.objects.exclude(status='published')
# Chaining filters
recent_posts = Post.objects.filter(
status='published'
).filter(
created_at__gte='2024-01-01'
).order_by('-views')[:10]
# UPDATE - Modifying data
post = Post.objects.get(id=1)
post.views += 1
post.save()
# Bulk update
Post.objects.filter(status='draft').update(status='published')
# DELETE - Removing data
post = Post.objects.get(id=1)
post.delete()
# Bulk delete
Post.objects.filter(status='draft').delete()
# ADVANCED QUERIES
# Count
post_count = Post.objects.filter(status='published').count()
# Aggregation
from django.db.models import Count, Avg, Max
stats = Post.objects.aggregate(
total=Count('id'),
avg_views=Avg('views'),
max_views=Max('views')
)
# Related data (following foreign keys)
post = Post.objects.get(id=1)
author_name = post.author.username
category_name = post.category.name
# Reverse relationships
user = User.objects.get(username='alice')
user_posts = user.post_set.all() # All posts by this user
Views: Handling Requests
Views are Python functions or classes that receive web requests and return web responses.
# blog/views.py
from django.shortcuts import render, get_object_or_404, redirect
from django.http import HttpResponse
from .models import Post, Category
# Function-based view
def post_list(request):
posts = Post.objects.filter(status='published')
return render(request, 'blog/post_list.html', {'posts': posts})
def post_detail(request, slug):
post = get_object_or_404(Post, slug=slug, status='published')
# Increment view count
post.views += 1
post.save()
return render(request, 'blog/post_detail.html', {'post': post})
# Class-based view (more powerful)
from django.views.generic import ListView, DetailView
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')
class PostDetailView(DetailView):
model = Post
template_name = 'blog/post_detail.html'
context_object_name = 'post'
def get_object(self):
post = super().get_object()
post.views += 1
post.save()
return post
URL Routing: Connecting URLs to Views
# blog/urls.py
from django.urls import path
from . import views
app_name = 'blog'
urlpatterns = [
path('', views.PostListView.as_view(), name='post_list'),
path('post//', views.PostDetailView.as_view(), name='post_detail'),
path('category//', views.category_posts, name='category_posts'),
]
# myproject/urls.py (main URL config)
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('blog/', include('blog.urls')),
]
# URL patterns support:
# - → integers
# - → slugs (letters, numbers, hyphens, underscores)
# - → strings
# - → UUIDs
# - → full paths with slashes
Templates: Dynamic HTML
Templates combine HTML with Django Template Language (DTL) to create dynamic pages.
<!-- templates/blog/post_list.html -->
{% extends 'base.html' %}
{% block title %}Blog Posts{% endblock %}
{% block content %}
<h1>Latest Blog Posts</h1>
{% for post in posts %}
<article>
<h2>
<a href="{% url 'blog:post_detail' post.slug %}">
{{ post.title }}
</a>
</h2>
<p class="meta">
By {{ post.author.username }} on {{ post.created_at|date:"F d, Y" }}
</p>
<p>{{ post.content|truncatewords:30 }}</p>
</article>
{% empty %}
<p>No posts available.</p>
{% endfor %}
<!-- Pagination -->
{% if is_paginated %}
<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 %}
Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}
{% 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 %}
{% endblock %}
<!-- templates/base.html -->
<!DOCTYPE html>
<html>
<head>
<title>{% block title %}My Site{% endblock %}</title>
</head>
<body>
<nav>
<a href="{% url 'blog:post_list' %}">Home</a>
</nav>
{% block content %}{% endblock %}
<footer>
<p>© 2024 My Blog</p>
</footer>
</body>
</html>
Django Admin: Automatic Admin Interface
One of Django's killer features - a production-ready admin interface with zero code!
# blog/admin.py
from django.contrib import admin
from .models import Post, Category
# Simple registration
admin.site.register(Category)
# Customized admin
@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
list_display = ['title', 'author', 'status', 'created_at', 'views']
list_filter = ['status', 'created_at', 'category']
search_fields = ['title', 'content']
prepopulated_fields = {'slug': ('title',)}
date_hierarchy = 'created_at'
ordering = ['-created_at']
# Customize form layout
fieldsets = (
('Basic Info', {
'fields': ('title', 'slug', 'author', 'category')
}),
('Content', {
'fields': ('content', 'status')
}),
('Metadata', {
'fields': ('views',),
'classes': ('collapse',)
}),
)
# Create superuser
python manage.py createsuperuser
# Visit http://127.0.0.1:8000/admin/
Forms: User Input Handling
# blog/forms.py
from django import forms
from .models import Post
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = ['title', 'slug', 'category', 'content', 'status']
widgets = {
'content': forms.Textarea(attrs={'rows': 10}),
}
def clean_title(self):
title = self.cleaned_data['title']
if len(title) < 10:
raise forms.ValidationError("Title must be at least 10 characters")
return title
# blog/views.py
from .forms import PostForm
def create_post(request):
if request.method == 'POST':
form = PostForm(request.POST)
if form.is_valid():
post = form.save(commit=False)
post.author = request.user
post.save()
return redirect('blog:post_detail', slug=post.slug)
else:
form = PostForm()
return render(request, 'blog/post_form.html', {'form': form})
Authentication: User Management
# Built-in authentication views
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'),
]
# Protecting views with login required
from django.contrib.auth.decorators import login_required
@login_required
def create_post(request):
# Only logged-in users can access this
pass
# Check permissions in templates
{% if user.is_authenticated %}
<a href="{% url 'create_post' %}">Create Post</a>
{% else %}
<a href="{% url 'login' %}">Login</a>
{% endif %}
Best Practices
- Use environment variables: Never hardcode secrets in settings.py
- Create multiple apps: Separate concerns (blog, users, api, etc.)
- Use Class-Based Views: For complex views with reusable logic
- Optimize queries: Use
select_related()andprefetch_related() - Use Django's forms: Built-in validation and security
- Write tests: Django has excellent testing tools
- Keep migrations in version control: Track database changes
- Use Django signals sparingly: Can make code hard to debug
- Follow the 12-factor app methodology: For deployment
Master Django with Expert Mentorship
Our Full Stack Python program covers Django from basics to building production-ready applications. Learn MTV architecture, ORM, authentication, and deployment with personalized guidance.
Explore Full Stack Python Program