Python Django 框架详尽教程
第一部分:Django 基础
1. Django 简介与安装
Django 是什么?
Django 是一个高级 Python Web 框架,遵循 MTV(Model-Template-View)设计模式,强调快速开发和 DRY(Don't Repeat Yourself)原则。
安装 Django
# 使用 pip 安装最新稳定版
pip install django
# 验证安装
python -m django --version
# 创建虚拟环境(推荐)
python -m venv myenv
source myenv/bin/activate # Linux/Mac
myenv\Scripts\activate # Windows
2. 创建第一个项目
django-admin startproject mysite
项目结构:
mysite/
manage.py
mysite/
__init__.py
settings.py
urls.py
asgi.py
wsgi.py
3. 开发服务器
python manage.py runserver
# 访问 http://127.0.0.1:8000/
4. 创建应用
python manage.py startapp blog
在 settings.py
中注册应用:
INSTALLED_APPS = [
...
'blog.apps.BlogConfig',
]
第二部分:模型与数据库
1. 定义模型
blog/models.py
:
from django.db import models
from django.contrib.auth.models import User
class Post(models.Model):
STATUS_CHOICES = [
('draft', 'Draft'),
('published', 'Published'),
]
title = models.CharField(max_length=250)
slug = models.SlugField(max_length=250, unique_for_date='publish')
author = models.ForeignKey(User, on_delete=models.CASCADE)
body = models.TextField()
publish = models.DateTimeField(default=timezone.now)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
status = models.CharField(max_length=10, choices=STATUS_CHOICES, default='draft')
class Meta:
ordering = ('-publish',)
def __str__(self):
return self.title
2. 数据库迁移
python manage.py makemigrations
python manage.py migrate
3. Django Shell
python manage.py shell
示例操作:
from blog.models import Post
from django.contrib.auth.models import User
user = User.objects.get(username='admin')
post = Post(title='First Post', slug='first-post', author=user, body='Post body.')
post.save()
Post.objects.all()
第三部分:视图与URL
1. 函数视图
blog/views.py
:
from django.shortcuts import render, get_object_or_404
from .models import Post
def post_list(request):
posts = Post.objects.filter(status='published')
return render(request, 'blog/post/list.html', {'posts': posts})
def post_detail(request, year, month, day, post):
post = get_object_or_404(
Post,
slug=post,
status='published',
publish__year=year,
publish__month=month,
publish__day=day
)
return render(request, 'blog/post/detail.html', {'post': post})
2. URL 配置
blog/urls.py
:
from django.urls import path
from . import views
app_name = 'blog'
urlpatterns = [
path('', views.post_list, name='post_list'),
path('<int:year>/<int:month>/<int:day>/<slug:post>/',
views.post_detail, name='post_detail'),
]
mysite/urls.py
:
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('blog/', include('blog.urls', namespace='blog')),
]
第四部分:模板系统
1. 模板配置
settings.py
:
TEMPLATES = [
{
'DIRS': [os.path.join(BASE_DIR, 'templates')],
...
}
]
2. 基础模板
templates/base.html
:
运行
<!DOCTYPE html>
<html>
<head>
<title>{% block title %}{% endblock %}</title>
</head>
<body>
<div id="content">
{% block content %}
{% endblock %}
</div>
</body>
</html>
3. 继承模板
templates/blog/post/list.html
:
运行
{% extends "base.html" %}
{% block title %}My Blog{% endblock %}
{% block content %}
<h1>My Blog</h1>
{% for post in posts %}
<h2>
<a href="{{ post.get_absolute_url }}">
{{ post.title }}
</a>
</h2>
<p class="date">
Published {{ post.publish }} by {{ post.author }}
</p>
{{ post.body|truncatewords:30|linebreaks }}
{% endfor %}
{% endblock %}
第五部分:表单处理
1. 创建表单
blog/forms.py
:
from django import forms
from .models import Comment
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ('name', 'email', 'body')
2. 处理表单的视图
blog/views.py
:
from .forms import CommentForm
def post_detail(request, year, month, day, post):
post = get_object_or_404(Post, slug=post,
status='published',
publish__year=year,
publish__month=month,
publish__day=day)
comments = post.comments.filter(active=True)
new_comment = None
if request.method == 'POST':
comment_form = CommentForm(data=request.POST)
if comment_form.is_valid():
new_comment = comment_form.save(commit=False)
new_comment.post = post
new_comment.save()
else:
comment_form = CommentForm()
return render(request,
'blog/post/detail.html',
{'post': post,
'comments': comments,
'new_comment': new_comment,
'comment_form': comment_form})
3. 表单模板
运行
<h2>Add a new comment</h2>
<form method="post">
{% csrf_token %}
{{ comment_form.as_p }}
<button type="submit">Add Comment</button>
</form>
第六部分:类视图
1. 列表视图
blog/views.py
:
from django.views.generic import ListView
class PostListView(ListView):
queryset = Post.published.all()
context_object_name = 'posts'
paginate_by = 3
template_name = 'blog/post/list.html'
2. 详情视图
from django.views.generic import DetailView
class PostDetailView(DetailView):
model = Post
template_name = 'blog/post/detail.html'
context_object_name = 'post'
def get_object(self, queryset=None):
post = super().get_object(queryset=queryset)
# 增加阅读计数等逻辑
return post
3. URL 配置
from django.urls import path
from .views import PostListView, PostDetailView
urlpatterns = [
path('', PostListView.as_view(), name='post_list'),
path('<int:year>/<int:month>/<int:day>/<slug:post>/',
PostDetailView.as_view(), name='post_detail'),
]
第七部分:用户认证
1. 登录视图
account/views.py
:
from django.contrib.auth import authenticate, login
from django.shortcuts import render, redirect
def user_login(request):
if request.method == 'POST':
form = LoginForm(request.POST)
if form.is_valid():
cd = form.cleaned_data
user = authenticate(request,
username=cd['username'],
password=cd['password'])
if user is not None:
if user.is_active:
login(request, user)
return HttpResponse('Authenticated successfully')
else:
return HttpResponse('Disabled account')
else:
return HttpResponse('Invalid login')
else:
form = LoginForm()
return render(request, 'account/login.html', {'form': form})
2. 使用 Django 内置认证视图
urls.py
:
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-change/done/',
auth_views.PasswordChangeDoneView.as_view(),
name='password_change_done'),
]
第八部分:Django Admin
1. 自定义 Admin
blog/admin.py
:
from django.contrib import admin
from .models import Post
@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
list_display = ('title', 'slug', 'author', 'publish', 'status')
list_filter = ('status', 'created', 'publish', 'author')
search_fields = ('title', 'body')
prepopulated_fields = {'slug': ('title',)}
raw_id_fields = ('author',)
date_hierarchy = 'publish'
ordering = ('status', 'publish')
2. 创建超级用户
python manage.py createsuperuser
第九部分:REST API (DRF)
1. 安装 DRF
pip install djangorestframework
2. 创建序列化器
blog/serializers.py
:
from rest_framework import serializers
from .models import Post
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = ['id', 'title', 'slug', 'author', 'body', 'publish', 'status']
3. 视图集
blog/views.py
:
from rest_framework import viewsets
from .models import Post
from .serializers import PostSerializer
class PostViewSet(viewsets.ModelViewSet):
queryset = Post.objects.all()
serializer_class = PostSerializer
4. 路由配置
mysite/urls.py
:
from django.urls import path, include
from rest_framework import routers
from blog import views
router = routers.DefaultRouter()
router.register(r'posts', views.PostViewSet)
urlpatterns = [
path('api/', include(router.urls)),
path('api-auth/', include('rest_framework.urls', namespace='rest_framework'))
]
第十部分:部署
1. 生产环境设置
settings.py
:
DEBUG = False
ALLOWED_HOSTS = ['yourdomain.com', 'www.yourdomain.com', 'localhost']
# 数据库配置
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'mydb',
'USER': 'myuser',
'PASSWORD': 'mypassword',
'HOST': 'localhost',
'PORT': '5432',
}
}
# 静态文件
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
STATIC_URL = '/static/'
2. 使用 Gunicorn 和 Nginx
安装 Gunicorn:
pip install gunicorn
创建 gunicorn.service
:
[Unit]
Description=gunicorn daemon
After=network.target
[Service]
User=username
Group=www-data
WorkingDirectory=/path/to/your/project
ExecStart=/path/to/venv/bin/gunicorn --access-logfile - --workers 3 --bind unix:/run/gunicorn.sock project.wsgi:application
[Install]
WantedBy=multi-user.target
Nginx 配置: nginx
server {
listen 80;
server_name yourdomain.com;
location = /favicon.ico { access_log off; log_not_found off; }
location /static/ {
root /path/to/your/project;
}
location / {
include proxy_params;
proxy_pass http://unix:/run/gunicorn.sock;
}
}
第十一部分:高级主题
1. 自定义中间件
middleware.py
:
class SimpleMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
# 请求前的处理
response = self.get_response(request)
# 响应后的处理
return response
2. 信号
models.py
:
from django.db.models.signals import post_save
from django.dispatch import receiver
@receiver(post_save, sender=Post)
def notify_post_saved(sender, instance, created, **kwargs):
if created:
print(f"New post created: {instance.title}")
3. 缓存
settings.py
:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.redis.RedisCache',
'LOCATION': 'redis://127.0.0.1:6379',
}
}
视图缓存:
from django.views.decorators.cache import cache_page
@cache_page(60 * 15)
def my_view(request):
...
第十二部分:测试
1. 测试模型
tests.py
:
from django.test import TestCase
from django.contrib.auth.models import User
from .models import Post
class PostModelTest(TestCase):
def setUp(self):
self.user = User.objects.create_user(username='testuser', password='12345')
Post.objects.create(title='Test title', body='Test body', author=self.user)
def test_post_content(self):
post = Post.objects.get(id=1)
self.assertEqual(post.title, 'Test title')
self.assertEqual(post.body, 'Test body')
self.assertEqual(str(post), 'Test title')
2. 测试视图
from django.urls import reverse
class PostViewTest(TestCase):
def setUp(self):
self.user = User.objects.create_user(username='testuser', password='12345')
Post.objects.create(title='Test title', body='Test body', author=self.user)
def test_view_url_exists_at_desired_location(self):
response = self.client.get('/blog/')
self.assertEqual(response.status_code, 200)
def test_view_uses_correct_template(self):
response = self.client.get(reverse('post_list'))
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'blog/post/list.html')
第十三部分:性能优化
1. 数据库优化
# 使用 select_related 优化外键查询
posts = Post.objects.select_related('author').all()
# 使用 prefetch_related 优化多对多关系
posts = Post.objects.prefetch_related('tags').all()
2. 缓存策略
# 模板片段缓存
{% load cache %}
{% cache 500 sidebar %}
... sidebar content ...
{% endcache %}
# 视图缓存
from django.views.decorators.cache import cache_page
@cache_page(60 * 15)
def my_view(request):
...
3. 使用 django-debug-toolbar
安装:
pip install django-debug-toolbar
配置:
INSTALLED_APPS = [
...
'debug_toolbar',
]
MIDDLEWARE = [
'debug_toolbar.middleware.DebugToolbarMiddleware',
...
]
INTERNAL_IPS = ['127.0.0.1']
第十四部分:安全最佳实践
- SECRET_KEY 保护: 永远不要提交到版本控制
- DEBUG 模式: 生产环境必须设置为 False
- ALLOWED_HOSTS: 生产环境必须设置
- HTTPS: 使用 SSL/TLS
- CSRF 保护: Django 默认启用
- XSS 防护: 使用模板自动转义
- SQL 注入防护: 使用 ORM 而非原始 SQL
- 点击劫持防护: 使用 X-Frame-Options
- 密码哈希: 使用强哈希算法
- 安全头部: 使用 django-secure 或 whitenoise
第十五部分:扩展 Django
1. 常用第三方包
- django-crispy-forms: 漂亮的表单渲染
- django-allauth: 完整的认证解决方案
- django-filter: 灵活的过滤
- django-taggit: 标签系统
- django-ckeditor: 富文本编辑器
- django-storages: 多种存储后端支持
- django-celery: 异步任务队列
- django-redis: Redis 缓存/会话支持
2. 创建可重用应用
- 保持应用功能单一
- 使用 setup.py 打包
- 提供良好的文档
- 编写全面的测试
- 发布到 PyPI
第十六部分:学习资源
- Django 官方文档
- Django Girls 教程
- MDN Django 教程
- Django REST Framework 文档
- Awesome Django
- Two Scoops of Django (书籍)
通过这个详尽教程,你应该已经掌握了 Django 的核心概念和高级功能。建议从一个小项目开始实践,逐步构建更复杂的应用。