Django is the most popular and complete web framework written in Python. Originally developed to power the Lawrence Journal-World newspaper, today it's used by companies like Instagram, Pinterest, Spotify, and Dropbox to build robust and scalable web applications.
In this complete guide, you will learn everything you need to start developing with Django, from basic concepts to advanced techniques that will make you a proficient developer.
What is Django?
Django is a high-level web framework written entirely in Python that follows the "batteries included" philosophy. This means it comes with everything you need to build a complete web application — from authentication to database management, from admin interface to form processing.
Django's philosophy is based on some fundamental principles that make it unique:
- DRY (Don't Repeat Yourself): Django encourages code reuse through its app-based architecture
- MTV (Model-Template-View): A clear architectural pattern that separates responsibilities
- Pragmatism: The framework was designed to help developers solve real-world problems quickly
- Explicit is better than implicit: Clear and readable code is preferred over "magic"
According to the official Django documentation, the framework was released in 2005 and has since been maintained by an active community of developers who regularly contribute with improvements and updates.
Why Use Django?
There are many reasons to choose Django as your web framework. Let's explore the main advantages that make it stand out from other Python frameworks like Flask or FastAPI.
1. Batteries Included
Unlike lightweight frameworks, Django comes with a comprehensive set of built-in features that would otherwise require additional libraries and configuration. This includes:
- Complete user authentication system
- Automatic admin interface (Django Admin)
- Form system with validation
- File upload handling
- Internationalization system (i18n)
- Protection against common vulnerabilities (CSRF, XSS, SQL Injection)
According to Real Python, the "batteries included" approach means you can focus on what really matters: building the unique features of your application, not reinventing the wheel.
2. Powerful ORM
Django's ORM (Object-Relational Mapper) is one of its most powerful features, allowing you to interact with databases using Python objects instead of writing SQL manually. This provides several benefits:
from django.db import models
class Product(models.Model):
name = models.CharField(max_length=200)
price = models.DecimalField(max_digits=10, decimal_places=2)
description = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
category = models.ForeignKey('Category', on_delete=models.CASCADE)
class Meta:
ordering = ['-created_at']
def __str__(self):
return self.name
With the ORM, you can perform complex database operations without writing a single line of SQL:
# Create a product
product = Product.objects.create(
name="Python Notebook",
price=3500.00,
description="Perfect for developers",
category=category
)
# Search products with advanced filters
products = Product.objects.filter(
price__lte=5000
).filter(
category__name__contains="Electronics"
).order_by('-price')
# Aggregations
from django.db.models import Avg, Count
avg_prices = Product.objects.aggregate(Avg('price'))
According to Django Documentation, the ORM supports multiple database backends including PostgreSQL, MySQL, SQLite, and Oracle.
3. Django Admin
One of Django's most impressive features is the automatically generated Admin interface. In minutes, you can have a fully functional admin panel to manage your data.
# models.py
from django.db import models
class Article(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
published = models.BooleanField(default=False)
publish_date = models.DateTimeField()
def __str__(self):
return self.title
# admin.py
from django.contrib import admin
from .models import Article
@admin.register(Article)
class ArticleAdmin(admin.ModelAdmin):
list_display = ['title', 'published', 'publish_date']
list_filter = ['published', 'publish_date']
search_fields = ['title', 'content']
date_hierarchy = 'publish_date'
With just these few lines, you'll have an admin where you can list, filter, search, edit, and create articles. Django Admin also supports inline editing, bulk actions, and much more.
4. Security
Django was designed with security in mind, protecting your applications against common vulnerabilities by default. According to OWASP, Django includes built-in protection against:
- SQL Injection: Parameterized queries prevent SQL injection attacks
- Cross-Site Scripting (XSS): Template auto-escaping protects against XSS
- Cross-Site Request Forgery (CSRF): CSRF tokens are automatically included in forms
- Clickjacking: X-Frame-Options middleware prevents clickjacking
- Password Storage: Secure password hashing with PBKDF2 by default
5. Community and Ecosystem
With over 15 years of existence, Django has a massive community and a rich ecosystem of packages. You can find packages for virtually any functionality you need, from SMS sending to payment processing.
According to PyPI, there are thousands of Django packages available, covering areas like:
- Authentication (django-allauth, django-rest-auth)
- APIs (Django REST Framework, GraphQL)
- Admin themes (django-jet, django-grappelli)
- E-commerce (django-oscar, saleor)
- CMS (Wagtail, django-cms)
Installing Django
Getting started with Django is straightforward. You can install using pip:
# Create virtual environment (recommended)
python -m venv venv
source venv/bin/activate # Linux/Mac
venv\Scripts\activate # Windows
# Install Django
pip install django
# Check installation
django-admin --version
Alternatively, you can create a new Django project using django-admin:
django-admin startproject myproject
cd myproject
python manage.py runserver
This will create a basic Django project structure. According to Python.org, it's highly recommended to use virtual environments to isolate your Django projects and avoid dependency conflicts.
Understanding Django Structure
A Django project is organized in a hierarchical structure that may seem complex at first, but makes perfect sense once you understand how the pieces fit together.
Projects vs Apps
It's crucial to understand the difference between project and app in Django:
- Project: The entire application configuration. It's the container that holds all your site settings
- App: A reusable component that performs a specific functionality. Can be reused in different projects
For example, an e-commerce project might have apps like "catalog", "cart", "orders", and "payments".
# Create an app
python manage.py startapp blog
MTV Architecture
Django follows the Model-Template-View (MTV) pattern, which is similar to MVC but with slightly different responsibilities:
- Model: Defines the data structure and interacts with the database
- Template: Responsible for the presentation layer (HTML)
- View: Contains the business logic and processes requests
According to MDN Web Docs, this Separation of Concerns makes code more maintainable and testable.
Creating Your First Django Application
Let's create a simple blog application to demonstrate Django's fundamental concepts. This example will cover models, views, URLs, and templates.
Step 1: Configure the Database
Django uses SQLite by default, which is perfect for development. To set up, you need to run migrations:
python manage.py migrate
This will create all the necessary database tables, including those for users, sessions, and auth. You can also customize your database by editing settings.py:
# settings.py
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'mydb',
'USER': 'myuser',
'PASSWORD': 'mypassword',
'HOST': 'localhost',
'PORT': '5432',
}
}
According to W3Schools, PostgreSQL is highly recommended for production environments due to its robustness and scalability.
Step 2: Create Models
Models are the foundation of your Django application. Let's create a simple blog model:
# 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)
description = models.TextField(blank=True)
class Meta:
verbose_name_plural = "Categories"
def __str__(self):
return self.name
class Post(models.Model):
title = models.CharField(max_length=200)
slug = models.SlugField(unique=True)
content = models.TextField()
author = models.ForeignKey(User, on_delete=models.CASCADE)
category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
published = models.BooleanField(default=False)
class Meta:
ordering = ['-created_at']
def __str__(self):
return self.title
After creating your models, you need to create migrations:
python manage.py makemigrations
python manage.py migrate
Step 3: Create Views
Views are where the application logic lives. Django supports both function-based views (FBV) and class-based views (CBV):
# blog/views.py
from django.shortcuts import render, get_object_or_404
from django.views.generic import ListView, DetailView
from django.models import Post
from .models import Category
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(published=True)
class PostDetailView(DetailView):
model = Post
template_name = 'blog/post_detail.html'
context_object_name = 'post'
def posts_by_category(request, slug):
category = get_object_or_404(Category, slug=slug)
posts = Post.objects.filter(published=True, category=category)
return render(request, 'blog/post_list.html', {
'posts': posts,
'category': category
})
Step 4: Configure URLs
URL configuration is where you map URLs to views:
# blog/urls.py
from django.urls import path
from .views import PostListView, PostDetailView, posts_by_category
urlpatterns = [
path('', PostListView.as_view(), name='post_list'),
path('post/<slug:slug>/', PostDetailView.as_view(), name='post_detail'),
path('category/<slug:slug>/', posts_by_category, name='posts_by_category'),
]
# myproject/urls.py (main)
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('blog/', include('blog.urls')),
]
Step 5: Create Templates
Templates are where you define the HTML that will be rendered. Django template language is powerful but easy to learn:
<!-- templates/blog/post_list.html -->
{% extends 'base.html' %}
{% block content %}
<h1>Blog</h1>
{% for post in posts %}
<article class="post-card">
<h2><a href="{% url 'post_detail' post.slug %}">{{ post.title }}</a></h2>
<p class="meta">
By {{ post.author.username }} on {{ post.created_at|date:"m/d/Y" }}
</p>
<p>{{ post.content|truncatewords:50 }}</p>
<a href="{% url 'post_detail' post.slug %}">Read more...</a>
</article>
{% empty %}
<p>No posts found.</p>
{% endfor %}
{% if page_obj.has_other_pages %}
<div class="pagination">
{% if page_obj.has_previous %}
<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>
{% endif %}
</div>
{% endif %}
{% endblock %}
Django REST Framework
To create RESTful APIs with Django, Django REST Framework (DRF) is the most popular choice. It extends Django's functionality to make building APIs straightforward.
# requirements.txt
djangorestframework>=3.14.0
# settings.py
INSTALLED_APPS = [
...
'rest_framework',
]
# serializers.py
from rest_framework import serializers
from .models import Post
class PostSerializer(serializers.ModelSerializer):
author = serializers.StringRelatedField()
category = serializers.StringRelatedField()
class Meta:
model = Post
fields = ['id', 'title', 'slug', 'content', 'author', 'category', 'created_at']
# views.py
from rest_framework import viewsets
from rest_framework.permissions import IsAuthenticatedOrReadOnly
from .models import Post
from .serializers import PostSerializer
class PostViewSet(viewsets.ModelViewSet):
queryset = Post.objects.filter(published=True)
serializer_class = PostSerializer
permission_classes = [IsAuthenticatedOrReadOnly]
def perform_create(self, serializer):
serializer.save(author=self.request.user)
# urls.py
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import PostViewSet
router = DefaultRouter()
router.register(r'posts', PostViewSet)
urlpatterns = [
path('api/', include(router.urls)),
]
According to Django REST Framework documentation, DRF provides features like authentication, serialization, validation, and viewsets that make building APIs much easier.
Authentication and Authorization
Django has a built-in authentication system that handles users, groups, permissions, and sessions. Let's explore how to use it effectively.
Creating a Login System
# views.py
from django.contrib.auth import login, authenticate, logout
from django.contrib.auth.decorators import login_required
from django.shortcuts import render, redirect
def login_view(request):
if request.method == 'POST':
username = request.POST['username']
password = request.POST['password']
user = authenticate(request, username=username, password=password)
if user is not None:
login(request, user)
return redirect('home')
else:
return render(request, 'login.html', {'error': 'Invalid credentials'})
return render(request, 'login.html')
@login_required
def profile_view(request):
return render(request, 'profile.html', {'user': request.user})
def logout_view(request):
logout(request)
return redirect('home')
<!-- templates/registration/login.html -->
<form method="post">
{% csrf_token %}
<input type="text" name="username" placeholder="Username" required>
<input type="password" name="password" placeholder="Password" required>
<button type="submit">Login</button>
</form>
Custom Permissions
You can create custom permissions to control access to views:
from rest_framework import permissions
class IsOwnerOrReadOnly(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
if request.method in permissions.SAFE_METHODS:
return True
return obj.author == request.user or request.user.is_staff
Testing Django Applications
Testing is essential to maintain quality code. Django provides a powerful testing framework integrated with unittest.
# blog/tests.py
from django.test import TestCase
from django.contrib.auth.models import User
from .models import Post, Category
from django.utils import timezone
from django.test import Client
class PostModelTest(TestCase):
def setUp(self):
self.user = User.objects.create_user(
username='testuser',
password='testpass123'
)
self.category = Category.objects.create(
name='Technology',
slug='technology'
)
def test_post_creation(self):
post = Post.objects.create(
title='Test Post',
slug='test-post',
content='Test content',
author=self.user,
category=self.category,
published=True
)
self.assertEqual(post.__str__(), 'Test Post')
self.assertTrue(post.published)
class PostViewTest(TestCase):
def setUp(self):
self.client = Client()
def test_post_list_view(self):
response = self.client.get('/blog/')
self.assertEqual(response.status_code, 200)
To run the tests:
python manage.py test
According to Django Testing Documentation, it's a best practice to write tests for all critical functionality of your application.
Deployment
When your application is ready for production, there are several deployment options. Let's cover the most popular ones.
Using Gunicorn
Gunicorn is a widely used WSGI HTTP server with Django:
pip install gunicorn
# Run with Gunicorn
gunicorn myproject.wsgi:application --bind 0.0.0.0:8000
Docker
Containerizing your Django application with Docker is highly recommended:
# Dockerfile
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8000
CMD ["gunicorn", "myproject.wsgi:application", "--bind", "0.0.0.0:8000"]
# docker-compose.yml
version: '3.8'
services:
web:
build: .
ports:
- "8000:8000"
environment:
- DEBUG=0
- ALLOWED_HOSTS=yoursite.com
depends_on:
- db
db:
image: postgres:15
environment:
- POSTGRES_DB=mydb
- POSTGRES_USER=myuser
- POSTGRES_PASSWORD=mypassword
volumes:
- pgdata:/var/lib/postgresql/data
Hosting Platforms
There are several platforms where you can deploy your Django application:
- Render: Easy deployment, good free tier
- Railway: Modern, easy to use
- Heroku: Classic choice, now paid
- DigitalOcean: VPS control, great for self-hosting
According to Stack Overflow, the choice of platform depends on your specific needs like budget, scalability requirements, and technical expertise.
Django vs Other Frameworks
It's important to understand when to use Django versus other Python frameworks. Here's a quick comparison:
- vs Flask: Django is more "batteries included", better for large applications. Flask is more lightweight, better for small projects or microservices
- vs FastAPI: FastAPI is faster and better for APIs. Django is better for full-stack applications with templating
- vs Pyramid: Pyramid is more flexible, Django is more opinionated with design decisions already made
For traditional web applications that need auth, admin, and database interaction, Django is usually the best choice.
Next Steps
Now that you've learned Django fundamentals, here are some next steps to continue your learning:
- FastAPI Python — explore another popular framework for APIs
- Python Requests — learn how to make HTTP requests
- Practice by creating your own projects
- Explore packages like django-allauth for social authentication
- Learn about caching with Redis
- Explore Django Channels for WebSockets
Django is a valuable skill for any Python developer. With this guide, you have a solid foundation to start building your own professional web applications. Keep practicing and exploring the official documentation to become an expert!