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']

第十四部分:安全最佳实践

  1. SECRET_KEY 保护: 永远不要提交到版本控制
  2. DEBUG 模式: 生产环境必须设置为 False
  3. ALLOWED_HOSTS: 生产环境必须设置
  4. HTTPS: 使用 SSL/TLS
  5. CSRF 保护: Django 默认启用
  6. XSS 防护: 使用模板自动转义
  7. SQL 注入防护: 使用 ORM 而非原始 SQL
  8. 点击劫持防护: 使用 X-Frame-Options
  9. 密码哈希: 使用强哈希算法
  10. 安全头部: 使用 django-secure 或 whitenoise

第十五部分:扩展 Django

1. 常用第三方包

  • django-crispy-forms: 漂亮的表单渲染
  • django-allauth: 完整的认证解决方案
  • django-filter: 灵活的过滤
  • django-taggit: 标签系统
  • django-ckeditor: 富文本编辑器
  • django-storages: 多种存储后端支持
  • django-celery: 异步任务队列
  • django-redis: Redis 缓存/会话支持

2. 创建可重用应用

  1. 保持应用功能单一
  2. 使用 setup.py 打包
  3. 提供良好的文档
  4. 编写全面的测试
  5. 发布到 PyPI

第十六部分:学习资源

  1. Django 官方文档
  2. Django Girls 教程
  3. MDN Django 教程
  4. Django REST Framework 文档
  5. Awesome Django
  6. Two Scoops of Django (书籍) 通过这个详尽教程,你应该已经掌握了 Django 的核心概念和高级功能。建议从一个小项目开始实践,逐步构建更复杂的应用。









results matching ""

    No results matching ""