TL;DR
Django 是 Python 中最完整的 Web 框架——内置 ORM、管理面板、认证、迁移和模板系统,开箱即用。需要快速开发关系型数据库应用时选择 Django。使用 DRF(Django REST Framework)构建 API。生产环境使用 Gunicorn + Nginx 部署,所有密钥使用环境变量管理。切勿在生产环境中使用 DEBUG=True。
核心要点
- 使用 python -m venv 创建虚拟环境,始终将依赖记录在 requirements.txt 或 pyproject.toml 中。
- Django ORM 的 select_related() 和 prefetch_related() 是解决 N+1 查询问题的关键工具。
- 生产环境必须设置 DEBUG=False、配置 ALLOWED_HOSTS 并使用环境变量管理 SECRET_KEY。
- Django REST Framework 的 ViewSet + Router 组合可以用最少的代码生成完整的 CRUD API。
- 使用 collectstatic 和 WhiteNoise(开发/小规模)或 Nginx(生产)提供静态文件。
- 数据库迁移文件必须提交到版本控制,并在部署时在启动应用之前运行。
1. 什么是 Django?
Django 是一个用 Python 编写的高级 Web 框架,由 Adrian Holovaty 和 Simon Willison 于 2003 年在 Lawrence Journal-World 报社开发,2005 年开源发布。Django 的核心理念是"开箱即用"(batteries-included):框架内置了你构建现代 Web 应用所需的几乎一切——ORM(对象关系映射)、URL 路由、模板引擎、表单处理、认证系统、管理后台和安全防护。
Django 遵循 MTV(Model-Template-View)架构模式,这是 MVC 模式的变体:Model 负责数据结构和数据库交互,Template 负责展示层,View 负责业务逻辑和请求处理。Django 的 URL 配置(urls.py)相当于传统 MVC 中的 Controller 路由层。
Django 被 Instagram、Pinterest、Disqus、Mozilla、National Geographic 等知名网站和公司使用。它特别适合内容管理系统、电子商务平台、新闻网站和需要复杂数据关系的 Web 应用。
2. 安装与项目初始化
始终在虚拟环境中安装 Django,避免污染全局 Python 环境。以下是完整的项目初始化流程:
# Create and activate a virtual environment
python -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
# Install Django
pip install django djangorestframework
pip freeze > requirements.txt
# Create a new Django project
django-admin startproject myproject .
# Create an application inside the project
python manage.py startapp blog
# Run the development server
python manage.py runserver
# Visit http://127.0.0.1:8000/项目创建后,需要在 settings.py 中注册新建的应用,并配置数据库连接:
# myproject/settings.py
import os
from pathlib import Path
BASE_DIR = Path(__file__).resolve().parent.parent
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = os.environ.get("SECRET_KEY", "django-insecure-dev-key-change-in-prod")
# SECURITY WARNING: do not run with debug turned on in production!
DEBUG = os.environ.get("DEBUG", "True") == "True"
ALLOWED_HOSTS = os.environ.get("ALLOWED_HOSTS", "localhost,127.0.0.1").split(",")
INSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
"rest_framework", # Django REST Framework
"blog", # Our application
]
# Database configuration (PostgreSQL recommended for production)
DATABASES = {
"default": {
"ENGINE": "django.db.backends.postgresql",
"NAME": os.environ.get("DB_NAME", "mydb"),
"USER": os.environ.get("DB_USER", "postgres"),
"PASSWORD": os.environ.get("DB_PASSWORD", ""),
"HOST": os.environ.get("DB_HOST", "localhost"),
"PORT": os.environ.get("DB_PORT", "5432"),
}
}
# Static files
STATIC_URL = "/static/"
STATIC_ROOT = BASE_DIR / "staticfiles"
MEDIA_URL = "/media/"
MEDIA_ROOT = BASE_DIR / "media"3. 模型与 ORM
Django 的 ORM(Object-Relational Mapper)允许你用 Python 类定义数据库表结构,并通过 Python 对象操作数据,而无需编写 SQL。每个 Model 类对应数据库中的一张表,每个类属性对应一个数据库字段。
# blog/models.py
from django.db import models
from django.contrib.auth.models import User
from django.utils import timezone
class Category(models.Model):
name = models.CharField(max_length=100, unique=True)
slug = models.SlugField(max_length=100, unique=True)
description = models.TextField(blank=True)
class Meta:
verbose_name_plural = "categories"
ordering = ["name"]
def __str__(self):
return self.name
class Post(models.Model):
STATUS_DRAFT = "draft"
STATUS_PUBLISHED = "published"
STATUS_CHOICES = [
(STATUS_DRAFT, "Draft"),
(STATUS_PUBLISHED, "Published"),
]
title = models.CharField(max_length=200)
slug = models.SlugField(max_length=200, unique_for_date="published_at")
author = models.ForeignKey(
User, on_delete=models.CASCADE, related_name="posts"
)
category = models.ForeignKey(
Category, on_delete=models.SET_NULL, null=True, related_name="posts"
)
tags = models.ManyToManyField("Tag", blank=True, related_name="posts")
body = models.TextField()
status = models.CharField(
max_length=10, choices=STATUS_CHOICES, default=STATUS_DRAFT
)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
published_at = models.DateTimeField(null=True, blank=True)
class Meta:
ordering = ["-published_at"]
indexes = [
models.Index(fields=["status", "published_at"]),
models.Index(fields=["author", "status"]),
]
def __str__(self):
return self.title
def publish(self):
self.status = self.STATUS_PUBLISHED
self.published_at = timezone.now()
self.save()
class Tag(models.Model):
name = models.CharField(max_length=50, unique=True)
slug = models.SlugField(max_length=50, unique=True)
def __str__(self):
return self.name定义模型后,运行迁移命令创建数据库表:
# Generate migration files from model changes
python manage.py makemigrations blog
# Apply migrations to the database
python manage.py migrate
# View migration SQL without applying
python manage.py sqlmigrate blog 0001Django QuerySet API 提供了强大的数据库查询接口:
# QuerySet API examples
from blog.models import Post, Category
from django.db.models import Count, Q, Prefetch
# Get all published posts
posts = Post.objects.filter(status="published")
# Chain filters
recent_posts = Post.objects.filter(
status="published",
published_at__year=2025
).order_by("-published_at")[:10]
# Complex Q object queries (OR conditions)
search_results = Post.objects.filter(
Q(title__icontains="django") | Q(body__icontains="django")
)
# Avoid N+1: use select_related for ForeignKey
posts_with_author = Post.objects.select_related("author", "category").all()
# Avoid N+1: use prefetch_related for ManyToMany
posts_with_tags = Post.objects.prefetch_related("tags").all()
# Aggregation: count posts per category
categories_with_count = Category.objects.annotate(
post_count=Count("posts", filter=Q(posts__status="published"))
).filter(post_count__gt=0)
# Update: bulk update without loading objects
Post.objects.filter(status="draft").update(status="archived")
# Get or create
category, created = Category.objects.get_or_create(
slug="python",
defaults={"name": "Python", "description": "Python programming"}
)4. 视图与 URL 路由
Django 支持两种视图类型:函数视图(FBV)和类视图(CBV)。函数视图更简单直接,类视图通过继承提供代码复用。
# blog/views.py
from django.shortcuts import render, get_object_or_404
from django.views.generic import ListView, DetailView, CreateView
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth.decorators import login_required
from django.http import JsonResponse
from .models import Post
# Function-Based View (FBV)
def post_list(request):
posts = Post.objects.filter(
status="published"
).select_related("author", "category")[:20]
return render(request, "blog/post_list.html", {"posts": posts})
# Class-Based View (CBV)
class PostListView(ListView):
model = Post
template_name = "blog/post_list.html"
context_object_name = "posts"
paginate_by = 20
def get_queryset(self):
return Post.objects.filter(
status="published"
).select_related("author", "category")
class PostDetailView(DetailView):
model = Post
template_name = "blog/post_detail.html"
slug_field = "slug"
def get_queryset(self):
return Post.objects.filter(status="published")
class PostCreateView(LoginRequiredMixin, CreateView):
model = Post
fields = ["title", "body", "category", "tags", "status"]
template_name = "blog/post_form.html"
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
# FBV with login required decorator
@login_required
def post_publish(request, pk):
post = get_object_or_404(Post, pk=pk, author=request.user)
post.publish()
return JsonResponse({"status": "published", "id": post.pk})URL 配置将 URL 模式映射到视图函数:
# blog/urls.py
from django.urls import path
from . import views
app_name = "blog" # Namespace for URL reversing
urlpatterns = [
path("", views.PostListView.as_view(), name="post-list"),
path("<slug:slug>/", views.PostDetailView.as_view(), name="post-detail"),
path("create/", views.PostCreateView.as_view(), name="post-create"),
path("<int:pk>/publish/", views.post_publish, name="post-publish"),
]
# myproject/urls.py (root URL configuration)
from django.contrib import admin
from django.urls import path, include
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [
path("admin/", admin.site.urls),
path("blog/", include("blog.urls", namespace="blog")),
path("api/", include("blog.api_urls")),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)5. 模板系统(DTL)
Django 模板语言(DTL)是一个功能完整的模板引擎,支持模板继承、自定义标签和过滤器。推荐的目录结构是在每个应用下创建 templates/app_name/ 子目录。
{# templates/base.html — base template using template inheritance #}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{% block title %}My Blog{% endblock %}</title>
{% load static %}
<link rel="stylesheet" href="{% static 'css/main.css' %}">
</head>
<body>
<nav>...</nav>
<main>
{% block content %}
{% endblock %}
</main>
<footer>...</footer>
</body>
</html>
{# templates/blog/post_list.html — child template #}
{% extends "base.html" %}
{% block title %}Blog Posts | My Site{% endblock %}
{% block content %}
<h1>Latest Posts</h1>
{# Loop through posts from context #}
{% for post in posts %}
<article>
<h2>
<a href="{% url 'blog:post-detail' post.slug %}">{{ post.title }}</a>
</h2>
<p>By {{ post.author.get_full_name }} on {{ post.published_at|date:"N j, Y" }}</p>
<p>{{ post.body|truncatewords:50 }}</p>
</article>
{% empty %}
<p>No posts yet.</p>
{% endfor %}
{# Pagination #}
{% if is_paginated %}
{% 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 %}
{% endif %}
{% endblock %}6. Django REST Framework(DRF)
Django REST Framework(DRF)是构建 Django API 的标准库,提供序列化器、APIView、ViewSet、权限系统和可浏览 API 界面。安装后在 INSTALLED_APPS 中添加 "rest_framework"。
# blog/serializers.py
from rest_framework import serializers
from .models import Post, Category, Tag
from django.contrib.auth.models import User
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ["id", "username", "first_name", "last_name"]
class TagSerializer(serializers.ModelSerializer):
class Meta:
model = Tag
fields = ["id", "name", "slug"]
class PostListSerializer(serializers.ModelSerializer):
author = UserSerializer(read_only=True)
category_name = serializers.CharField(
source="category.name", read_only=True
)
tags = TagSerializer(many=True, read_only=True)
tag_ids = serializers.PrimaryKeyRelatedField(
source="tags", queryset=Tag.objects.all(),
many=True, write_only=True
)
class Meta:
model = Post
fields = [
"id", "title", "slug", "author", "category", "category_name",
"tags", "tag_ids", "status", "published_at", "created_at"
]
read_only_fields = ["author", "created_at"]
def create(self, validated_data):
tags = validated_data.pop("tags", [])
post = Post.objects.create(**validated_data)
post.tags.set(tags)
return postViewSet 和 Router 是 DRF 中最高效的 API 构建方式:
# blog/api_views.py
from rest_framework import viewsets, permissions, filters
from rest_framework.decorators import action
from rest_framework.response import Response
from django_filters.rest_framework import DjangoFilterBackend
from .models import Post
from .serializers import PostListSerializer
class PostViewSet(viewsets.ModelViewSet):
"""Full CRUD API for posts."""
queryset = Post.objects.select_related("author", "category").prefetch_related("tags")
serializer_class = PostListSerializer
permission_classes = [permissions.IsAuthenticatedOrReadOnly]
filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
filterset_fields = ["status", "category", "author"]
search_fields = ["title", "body"]
ordering_fields = ["published_at", "created_at"]
def perform_create(self, serializer):
serializer.save(author=self.request.user)
@action(detail=True, methods=["post"],
permission_classes=[permissions.IsAuthenticated])
def publish(self, request, pk=None):
post = self.get_object()
if post.author != request.user:
return Response({"error": "Not authorized"}, status=403)
post.publish()
return Response({"status": "published"})
# blog/api_urls.py
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .api_views import PostViewSet
router = DefaultRouter()
router.register(r"posts", PostViewSet)
urlpatterns = [
path("", include(router.urls)),
]
# This generates:
# GET /api/posts/ -> list
# POST /api/posts/ -> create
# GET /api/posts/{id}/ -> retrieve
# PUT /api/posts/{id}/ -> update
# PATCH /api/posts/{id}/ -> partial_update
# DELETE /api/posts/{id}/ -> destroy
# POST /api/posts/{id}/publish/ -> custom action7. 认证与权限
Django 内置了完整的认证系统,包括用户模型、登录/登出视图、密码哈希和会话管理。对于 API,推荐使用 JWT(JSON Web Token)认证,使用 djangorestframework-simplejwt 库。
# Install JWT library
# pip install djangorestframework-simplejwt
# settings.py — configure JWT authentication
from datetime import timedelta
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": [
"rest_framework_simplejwt.authentication.JWTAuthentication",
],
"DEFAULT_PERMISSION_CLASSES": [
"rest_framework.permissions.IsAuthenticatedOrReadOnly",
],
"DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.PageNumberPagination",
"PAGE_SIZE": 20,
"DEFAULT_THROTTLE_CLASSES": [
"rest_framework.throttling.AnonRateThrottle",
"rest_framework.throttling.UserRateThrottle",
],
"DEFAULT_THROTTLE_RATES": {
"anon": "100/hour",
"user": "1000/hour",
},
}
SIMPLE_JWT = {
"ACCESS_TOKEN_LIFETIME": timedelta(minutes=60),
"REFRESH_TOKEN_LIFETIME": timedelta(days=7),
"ROTATE_REFRESH_TOKENS": True,
"BLACKLIST_AFTER_ROTATION": True,
"ALGORITHM": "HS256",
"AUTH_HEADER_TYPES": ("Bearer",),
}
# urls.py — add JWT token endpoints
from rest_framework_simplejwt.views import (
TokenObtainPairView,
TokenRefreshView,
TokenVerifyView,
)
urlpatterns += [
path("api/auth/token/", TokenObtainPairView.as_view(), name="token_obtain_pair"),
path("api/auth/token/refresh/", TokenRefreshView.as_view(), name="token_refresh"),
path("api/auth/token/verify/", TokenVerifyView.as_view(), name="token_verify"),
]DRF 提供细粒度的权限控制,可以自定义权限类:
# blog/permissions.py
from rest_framework import permissions
class IsAuthorOrReadOnly(permissions.BasePermission):
"""Allow only the author of an object to modify it."""
def has_object_permission(self, request, view, obj):
# Read permissions are allowed to any request
if request.method in permissions.SAFE_METHODS:
return True
# Write permissions only to the author
return obj.author == request.user
class IsAdminOrReadOnly(permissions.BasePermission):
"""Allow only admin users to modify; read-only for others."""
def has_permission(self, request, view):
if request.method in permissions.SAFE_METHODS:
return True
return request.user and request.user.is_staff8. 生产部署
生产部署 Django 的标准方案是:Gunicorn(WSGI 服务器)+ Nginx(反向代理)。Docker 使部署更加一致和可移植。以下是完整的 Docker 配置:
# Dockerfile
FROM python:3.12-slim
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
PIP_NO_CACHE_DIR=1
WORKDIR /app
# Install system dependencies
RUN apt-get update && apt-get install -y \
postgresql-client \
&& rm -rf /var/lib/apt/lists/*
# Install Python dependencies
COPY requirements.txt .
RUN pip install --upgrade pip && pip install -r requirements.txt
# Copy project
COPY . .
# Collect static files
RUN python manage.py collectstatic --noinput
EXPOSE 8000
CMD ["gunicorn", "myproject.wsgi:application", \
"--bind", "0.0.0.0:8000", \
"--workers", "4", \
"--worker-class", "gthread", \
"--threads", "2", \
"--timeout", "60", \
"--access-logfile", "-"]# docker-compose.yml
version: "3.9"
services:
db:
image: postgres:16-alpine
volumes:
- postgres_data:/var/lib/postgresql/data/
environment:
POSTGRES_DB: ${DB_NAME}
POSTGRES_USER: ${DB_USER}
POSTGRES_PASSWORD: ${DB_PASSWORD}
redis:
image: redis:7-alpine
web:
build: .
command: >
sh -c "python manage.py migrate &&
gunicorn myproject.wsgi:application
--bind 0.0.0.0:8000 --workers 4"
volumes:
- static_volume:/app/staticfiles
- media_volume:/app/media
env_file: .env
depends_on:
- db
- redis
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf
- static_volume:/static
- media_volume:/media
depends_on:
- web
volumes:
postgres_data:
static_volume:
media_volume:# nginx.conf
upstream django {
server web:8000;
}
server {
listen 80;
server_name yourdomain.com;
client_max_body_size 20M;
location /static/ {
alias /static/;
expires 1y;
add_header Cache-Control "public, immutable";
}
location /media/ {
alias /media/;
}
location / {
proxy_pass http://django;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}生产环境的 .env 文件应包含所有敏感配置,永远不要提交到版本控制:
# .env (add to .gitignore!)
SECRET_KEY=your-long-random-secret-key-here
DEBUG=False
ALLOWED_HOSTS=yourdomain.com,www.yourdomain.com
DB_NAME=mydb
DB_USER=dbuser
DB_PASSWORD=your-strong-db-password
DB_HOST=db
DB_PORT=5432
REDIS_URL=redis://redis:6379/0
EMAIL_HOST=smtp.sendgrid.net
EMAIL_PORT=587
EMAIL_HOST_USER=apikey
EMAIL_HOST_PASSWORD=your-sendgrid-api-key
DEFAULT_FROM_EMAIL=noreply@yourdomain.com9. Django vs Flask vs FastAPI vs Ruby on Rails
选择 Web 框架时,需要根据项目规模、团队经验、性能需求和生态系统来决策。以下对比四种主流框架的关键维度:
| 特性 | Django | Flask | FastAPI | Rails |
|---|---|---|---|---|
| 语言 | Python | Python | Python | Ruby |
| 类型 | 全栈 | 微框架 | API 框架 | 全栈 |
| 内置 ORM | ✓ | ✗ | ✗ | ✓ |
| 管理后台 | ✓ | ✗ | ✗ | ✗ |
| 自动迁移 | ✓ | 插件 | 需 Alembic | ✓ |
| REST API | DRF | Flask-RESTful | 内置 | Native |
| 异步支持 | Django 3.1+ | 有限 | 原生 | 有限 |
| 性能(吞吐量) | 中等 | 中等 | 高(异步) | 中等 |
| 学习曲线 | 中等 | 低 | 低 | 中等 |
| 文档质量 | 优秀 | 良好 | 优秀 | 优秀 |
| 社区规模 | 大 | 大 | 快速增长 | 大 |
| 适合场景 | 全功能 Web 应用 | 小型服务 | 高性能 API | 快速原型 |
决策指南:如果你需要快速构建包含管理后台、用户认证和数据库操作的 Web 应用,选 Django。如果你在构建高并发的纯 API 服务,考虑 FastAPI。如果你需要最大灵活性或者项目非常简单,选 Flask。如果你的团队有 Ruby 背景或者在构建 SaaS MVP,选 Rails。
10. Django Admin 与信号系统
Django 自动生成的管理后台(Admin)是其最强大的特性之一。通过简单的配置,你可以获得功能完整的数据管理界面,支持搜索、过滤、批量操作和自定义视图。
# blog/admin.py
from django.contrib import admin
from .models import Post, Category, Tag
@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
list_display = ["title", "author", "category", "status", "published_at"]
list_filter = ["status", "category", "author", "created_at"]
search_fields = ["title", "body"]
prepopulated_fields = {"slug": ("title",)}
raw_id_fields = ["author"]
date_hierarchy = "published_at"
ordering = ["-created_at"]
actions = ["make_published", "make_draft"]
@admin.action(description="Mark selected posts as published")
def make_published(self, request, queryset):
updated = queryset.update(status="published")
self.message_user(request, f"{updated} posts marked as published.")
@admin.action(description="Mark selected posts as draft")
def make_draft(self, request, queryset):
queryset.update(status="draft")
@admin.register(Category)
class CategoryAdmin(admin.ModelAdmin):
list_display = ["name", "slug"]
prepopulated_fields = {"slug": ("name",)}
# Django Signals — decoupled event handling
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.core.mail import send_mail
@receiver(post_save, sender=Post)
def notify_on_publish(sender, instance, created, **kwargs):
"""Send notification email when a post is published."""
if not created and instance.status == "published":
send_mail(
subject=f"New post published: {instance.title}",
message=f"Check out the new post: {instance.title}",
from_email="noreply@example.com",
recipient_list=["editor@example.com"],
fail_silently=True,
)常见问题
Django 和 Flask 哪个更好?
Django 更适合需要完整功能的大型项目:内置 ORM、管理面板、认证系统和迁移工具。Flask 更适合小型微服务或需要高度定制的场景。如果你在构建 CRUD 应用或需要快速搭建原型,Django 的"开箱即用"优势会节省大量时间。
Django ORM 性能如何?能处理大规模数据吗?
Django ORM 在大多数场景下性能出色,但需要注意 N+1 查询问题。使用 select_related() 处理 ForeignKey,使用 prefetch_related() 处理 ManyToMany 关系。对于复杂查询可以使用 .raw() 或 django.db.connection.cursor()。配合数据库索引和 QuerySet 的 .only()/.defer() 进一步优化。
Django REST Framework 和 FastAPI 如何选择?
如果你的应用需要高并发异步处理(如 WebSocket、实时数据流)或者只需要构建轻量级 API 而不需要 Django 其他功能,FastAPI 是更好的选择。如果你已经在使用 Django 或需要 Django 的 ORM、Admin、Auth 等功能,DRF 是自然的选择,集成更紧密。
如何在 Django 中处理大文件上传?
对于大文件上传,在 settings.py 中设置 FILE_UPLOAD_MAX_MEMORY_SIZE 和 DATA_UPLOAD_MAX_MEMORY_SIZE。使用 FileField 或 ImageField 存储文件,生产环境推荐配合 django-storages 将文件存储到 S3 或其他云存储。使用 chunked uploads(分块上传)处理超大文件,可以考虑 django-chunked-upload 库。
Django 如何做数据库迁移?
Django 的迁移系统会自动检测 models.py 的变更并生成迁移文件。使用 makemigrations 生成迁移文件,migrate 执行迁移。迁移文件应提交到版本控制中。生产环境部署时,在启动应用前运行 migrate。如需回滚,使用 migrate app_name migration_number 指定迁移版本。
如何在 Django 中实现缓存?
Django 支持多种缓存后端:内存缓存(适合开发)、Memcached 和 Redis(适合生产)。使用 django-redis 集成 Redis 缓存。可以缓存整个视图(@cache_page)、模板片段({% cache %})或手动使用 cache.get()/cache.set()。数据库查询结果缓存是最常见的优化点。
Django 支持异步视图吗?
从 Django 3.1 开始,Django 支持异步视图、中间件和 ORM 操作。使用 async def 定义异步视图,配合 ASGI 服务器(如 Daphne 或 Uvicorn)运行。注意:Django ORM 的异步支持需要使用 sync_to_async 包装同步 ORM 调用,或使用原生异步 ORM 方法(Django 4.1+)。
Django 项目如何处理多租户(Multi-tenancy)?
Django 多租户主要有三种方案:1) 行级隔离:每个模型添加 tenant 外键,查询时过滤;2) Schema 隔离:每个租户独立的 PostgreSQL schema,使用 django-tenant-schemas 或 django-tenants 库;3) 独立数据库:每个租户独立数据库,使用 Django 的多数据库路由。大多数 SaaS 应用推荐方案 2,兼顾隔离性和资源效率。
总结
Django 是 Python 生态中最成熟、最完整的 Web 框架。其"开箱即用"的设计哲学让开发者可以专注于业务逻辑而非基础设施。核心要点:使用虚拟环境管理依赖,优化 ORM 查询避免 N+1 问题,使用 DRF 构建标准化的 REST API,生产环境使用 Docker + Gunicorn + Nginx,始终通过环境变量管理密钥。掌握 Django 将让你有能力独立构建从简单博客到复杂电商平台的任何 Web 应用。