Django
The Web framework for perfectionists with deadlines - a full-stack framework with batteries included. Built-in ORM, admin interface, and authentication features.
GitHub Overview
django/django
The Web framework for perfectionists with deadlines.
Topics
Star History
Framework
Django
Overview
Django is a high-level Python web framework that enables rapid development with practical, clean design principles.
Details
Django is a web framework written in Python that was originally developed in 2005 at a newspaper company in Lawrence, Kansas, and was open-sourced by the Django Software Foundation in 2008. Based on the "batteries included" philosophy, it provides all the tools needed for web development out of the box. Django follows a Model-View-Template (MVT) architecture and includes an Object-Relational Mapping (ORM) that abstracts database operations, an automatically generated admin interface, URL routing, template engine, form processing, authentication system, and security features. Designed with scalability and security in mind, Django is used by large-scale services like Instagram, Pinterest, Mozilla, and NASA. Following the "Don't Repeat Yourself" (DRY) principle and "Convention over Configuration" approach, it enables efficient development.
Pros and Cons
Pros
- Comprehensive Framework: Rich built-in features including authentication, admin interface, ORM, and more
- High Security: Built-in protection against SQL injection, XSS, CSRF, and other vulnerabilities
- Automatic Admin Interface: Auto-generated admin interface from database models
- Excellent ORM: Database abstraction improves maintainability and portability
- Scalability: Well-suited for developing and operating large-scale web applications
- Rich Documentation: Detailed and well-written official documentation
- Active Community: Large community and extensive third-party packages
Cons
- Learning Curve: High barrier to entry for beginners due to comprehensive features
- Weight: Excessive functionality for small projects
- Flexibility Constraints: Must follow framework conventions
- Performance: May be slower than Ruby on Rails in some cases
- Monolithic Structure: Not ideal for microservices architecture
Key Links
- Django Official Site
- Django Documentation
- Django GitHub Repository
- Django Tutorial
- Django Packages
- Django Community
Code Examples
Hello World
# urls.py
from django.contrib import admin
from django.urls import path
from django.http import HttpResponse
def hello_world(request):
return HttpResponse("Hello, Django World!")
urlpatterns = [
path('admin/', admin.site.urls),
path('', hello_world, name='hello'),
]
# settings.py (basic configuration)
SECRET_KEY = 'your-secret-key-here'
DEBUG = True
ALLOWED_HOSTS = []
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
# Project creation commands
# django-admin startproject myproject
# cd myproject
# python manage.py runserver
Model Definition and ORM
# models.py
from django.db import models
from django.contrib.auth.models import User
class Category(models.Model):
name = models.CharField(max_length=100)
description = models.TextField(blank=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):
title = models.CharField(max_length=200)
content = models.TextField()
author = models.ForeignKey(User, on_delete=models.CASCADE)
category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True)
tags = models.ManyToManyField('Tag', blank=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
is_published = models.BooleanField(default=False)
class Meta:
ordering = ['-created_at']
def __str__(self):
return self.title
class Tag(models.Model):
name = models.CharField(max_length=50, unique=True)
def __str__(self):
return self.name
# ORM usage examples
# Create and run migrations
# python manage.py makemigrations
# python manage.py migrate
# Database operations
from myapp.models import Post, Category, Tag
# Create data
category = Category.objects.create(name="Technology", description="Tech-related articles")
post = Post.objects.create(
title="Getting Started with Django",
content="Django is an amazing framework.",
author_id=1,
category=category,
is_published=True
)
# Query examples
all_posts = Post.objects.all()
published_posts = Post.objects.filter(is_published=True)
recent_posts = Post.objects.filter(created_at__gte='2024-01-01')
tech_posts = Post.objects.filter(category__name="Technology")
# Complex queries
from django.db.models import Q, Count
popular_posts = Post.objects.annotate(
tag_count=Count('tags')
).filter(
Q(is_published=True) & Q(tag_count__gt=2)
).order_by('-created_at')
URL Routing
# myproject/urls.py (main URLconf)
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('blog.urls')),
path('api/', include('api.urls')),
]
# blog/urls.py
from django.urls import path
from . import views
app_name = 'blog'
urlpatterns = [
# Basic URLs
path('', views.index, name='index'),
path('posts/', views.post_list, name='post_list'),
# URLs with parameters
path('posts/<int:post_id>/', views.post_detail, name='post_detail'),
path('category/<slug:category_slug>/', views.category_posts, name='category_posts'),
# Regular expression URLs
path('archive/<int:year>/<int:month>/', views.archive, name='archive'),
# Named groups
path('tag/<str:tag_name>/', views.posts_by_tag, name='posts_by_tag'),
]
# views.py
from django.shortcuts import render, get_object_or_404
from django.http import HttpResponse
from .models import Post, Category
def index(request):
recent_posts = Post.objects.filter(is_published=True)[:5]
return render(request, 'blog/index.html', {'posts': recent_posts})
def post_detail(request, post_id):
post = get_object_or_404(Post, id=post_id, is_published=True)
return render(request, 'blog/post_detail.html', {'post': post})
def category_posts(request, category_slug):
category = get_object_or_404(Category, slug=category_slug)
posts = Post.objects.filter(category=category, is_published=True)
return render(request, 'blog/category_posts.html', {
'category': category,
'posts': posts
})
Template Processing
<!-- templates/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 'blog:index' %}">Home</a>
<a href="{% url 'blog:post_list' %}">All Posts</a>
</nav>
</header>
<main>
{% block content %}
{% endblock %}
</main>
<footer>
<p>© 2024 My Blog</p>
</footer>
</body>
</html>
<!-- templates/blog/index.html -->
{% extends 'base.html' %}
{% load static %}
{% block title %}Home - {{ block.super }}{% endblock %}
{% block content %}
<h1>Latest Posts</h1>
{% if posts %}
{% for post in posts %}
<article class="post-preview">
<h2>
<a href="{% url 'blog:post_detail' post.id %}">{{ post.title }}</a>
</h2>
<p class="post-meta">
Author: {{ post.author.username }} |
Category:
{% if post.category %}
<a href="{% url 'blog:category_posts' post.category.slug %}">
{{ post.category.name }}
</a>
{% else %}
Uncategorized
{% endif %} |
{{ post.created_at|date:"F j, Y" }}
</p>
<p>{{ post.content|truncatewords:30 }}</p>
{% if post.tags.all %}
<div class="tags">
Tags:
{% for tag in post.tags.all %}
<a href="{% url 'blog:posts_by_tag' tag.name %}" class="tag">
{{ tag.name }}
</a>
{% if not forloop.last %}, {% endif %}
{% endfor %}
</div>
{% endif %}
</article>
{% endfor %}
{% else %}
<p>No posts available yet.</p>
{% endif %}
<!-- Using custom filters -->
<p>{{ post.content|linebreaks|safe }}</p>
<p>Posted {{ post.created_at|timesince }} ago</p>
{% endblock %}
Form Processing
# forms.py
from django import forms
from django.contrib.auth.models import User
from .models import Post, Category
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = ['title', 'content', 'category', 'tags', 'is_published']
widgets = {
'title': forms.TextInput(attrs={
'class': 'form-control',
'placeholder': 'Enter title'
}),
'content': forms.Textarea(attrs={
'class': 'form-control',
'rows': 10,
'placeholder': 'Enter content'
}),
'category': forms.Select(attrs={'class': 'form-control'}),
'tags': forms.CheckboxSelectMultiple(),
'is_published': forms.CheckboxInput(attrs={'class': 'form-check-input'})
}
def clean_title(self):
title = self.cleaned_data['title']
if len(title) < 5:
raise forms.ValidationError('Title must be at least 5 characters long.')
return title
def clean_content(self):
content = self.cleaned_data['content']
if len(content) < 50:
raise forms.ValidationError('Content must be at least 50 characters long.')
return content
class ContactForm(forms.Form):
name = forms.CharField(
max_length=100,
label='Name',
widget=forms.TextInput(attrs={
'class': 'form-control',
'placeholder': 'Enter your name'
})
)
email = forms.EmailField(
label='Email',
widget=forms.EmailInput(attrs={
'class': 'form-control',
'placeholder': '[email protected]'
})
)
subject = forms.CharField(
max_length=200,
label='Subject',
widget=forms.TextInput(attrs={'class': 'form-control'})
)
message = forms.CharField(
label='Message',
widget=forms.Textarea(attrs={
'class': 'form-control',
'rows': 6,
'placeholder': 'Enter your message'
})
)
# views.py
from django.shortcuts import render, redirect, get_object_or_404
from django.contrib.auth.decorators import login_required
from django.contrib import messages
from django.core.mail import send_mail
from .forms import PostForm, ContactForm
@login_required
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()
form.save_m2m() # Save Many-to-Many fields
messages.success(request, 'Post created successfully.')
return redirect('blog:post_detail', post_id=post.id)
else:
form = PostForm()
return render(request, 'blog/create_post.html', {'form': form})
def contact(request):
if request.method == 'POST':
form = ContactForm(request.POST)
if form.is_valid():
# Send email
name = form.cleaned_data['name']
email = form.cleaned_data['email']
subject = form.cleaned_data['subject']
message = form.cleaned_data['message']
full_message = f"""
Name: {name}
Email: {email}
Message:
{message}
"""
send_mail(
subject=f"[Contact] {subject}",
message=full_message,
from_email=email,
recipient_list=['[email protected]'],
fail_silently=False,
)
messages.success(request, 'Contact message sent successfully.')
return redirect('blog:contact')
else:
form = ContactForm()
return render(request, 'blog/contact.html', {'form': form})
Admin Interface
# admin.py
from django.contrib import admin
from django.utils.html import format_html
from .models import Post, Category, Tag
@admin.register(Category)
class CategoryAdmin(admin.ModelAdmin):
list_display = ['name', 'post_count', 'created_at']
search_fields = ['name', 'description']
list_filter = ['created_at']
ordering = ['name']
def post_count(self, obj):
return obj.post_set.count()
post_count.short_description = 'Post Count'
@admin.register(Tag)
class TagAdmin(admin.ModelAdmin):
list_display = ['name', 'post_count']
search_fields = ['name']
def post_count(self, obj):
return obj.post_set.count()
post_count.short_description = 'Usage Count'
@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
list_display = [
'title', 'author', 'category', 'is_published',
'created_at', 'view_on_site'
]
list_filter = [
'is_published', 'created_at', 'category', 'tags'
]
search_fields = ['title', 'content', 'author__username']
date_hierarchy = 'created_at'
ordering = ['-created_at']
# Layout for detail view
fieldsets = (
('Basic Information', {
'fields': ('title', 'content')
}),
('Classification', {
'fields': ('category', 'tags'),
'classes': ('collapse',)
}),
('Settings', {
'fields': ('is_published',),
'classes': ('collapse',)
}),
('Meta Information', {
'fields': ('author', 'created_at', 'updated_at'),
'classes': ('collapse',),
}),
)
# Read-only fields
readonly_fields = ['created_at', 'updated_at']
# Custom queryset
def get_queryset(self, request):
qs = super().get_queryset(request)
if request.user.is_superuser:
return qs
return qs.filter(author=request.user)
# Custom save behavior
def save_model(self, request, obj, form, change):
if not change: # New object
obj.author = request.user
super().save_model(request, obj, form, change)
# Custom actions
def make_published(self, request, queryset):
count = queryset.update(is_published=True)
self.message_user(request, f'{count} posts were published.')
make_published.short_description = 'Publish selected posts'
def make_unpublished(self, request, queryset):
count = queryset.update(is_published=False)
self.message_user(request, f'{count} posts were unpublished.')
make_unpublished.short_description = 'Unpublish selected posts'
actions = [make_published, make_unpublished]
# Custom display
def view_on_site(self, obj):
if obj.is_published:
return format_html(
'<a href="/posts/{}/" target="_blank">View on Site</a>',
obj.id
)
return '-'
view_on_site.short_description = 'Site View'
# Customize admin site
admin.site.site_header = 'My Blog Admin'
admin.site.site_title = 'Blog Admin'
admin.site.index_title = 'Dashboard'
# Create superuser command
# python manage.py createsuperuser