摘要钩子:学完Django基础后,你是否感觉项目依然停留在“玩具”阶段?面对企业级的复杂需求,如何让代码既优雅又高效?本文深入解析Django的15项高级特性,带你从“会用”到“精通”,掌握构建千万级用户系统的核心技巧。我们将通过一个完整的企业级CMS项目实战,手把手教你使用中间件、信号、缓存、ORM高级查询等关键技术,让你的Django技能直接对标企业招聘要求!
开篇:为什么你的Django项目依然是“玩具”?
很多开发者学完Django基础教程后,信心满满地搭建了自己的博客或小商城,但在面试时却被问得哑口无言:
- “你的项目如何应对高并发?”
- “数据库查询优化做了哪些?”
- “系统的安全防护措施是什么?”
- “有没有实现读写分离?”
如果你也遇到了类似的问题,那么这篇文章就是为你准备的。我们将从企业级开发的角度,深入解析Django的各项高级特性,让你真正掌握构建复杂、高性能应用的技巧。
先来看一个真实的场景:某电商平台日活百万,你的任务是优化商品列表页的加载速度。目前每次请求需要查询数据库、计算库存、生成缓存,响应时间超过2秒。如何优化到200毫秒以内?
通过本文的学习,你将掌握解决这类问题的完整方案。
第一部分:企业级项目架构设计
1.1 分层架构与清晰边界
企业级项目与个人项目最大的区别在于架构的规范性。我们推荐采用六边形架构(端口与适配器) 结合清洁架构的设计理念。
项目结构规范:
enterprise_cms/
├── config/ # 配置层
│ ├── __init__.py
│ ├── settings/ # 多环境配置分离
│ │ ├── base.py # 基础配置
│ │ ├── development.py # 开发环境
│ │ ├── production.py # 生产环境
│ │ └── testing.py # 测试环境
│ ├── urls.py
│ ├── asgi.py
│ └── wsgi.py
├── apps/ # 业务应用层
│ ├── core/ # 核心模块(用户、权限等)
│ ├── articles/ # 文章管理模块
│ ├── comments/ # 评论模块
│ └── analytics/ # 数据分析模块
├── infrastructure/ # 基础设施层
│ ├── persistence/ # 数据持久化
│ ├── caching/ # 缓存服务
│ └── messaging/ # 消息服务
├── interfaces/ # 接口层
│ ├── api/ # REST API
│ ├── web/ # Web界面
│ └── admin/ # 管理后台
└── shared/ # 共享组件
├── utils/ # 工具函数
├── constants/ # 常量定义
└── exceptions/ # 异常处理
1.2 多环境配置管理
企业级项目必须支持开发、测试、生产等多环境。Django的配置分离是基础要求。
config/settings/base.py(基础配置):
import os
from pathlib import Path
from django.core.exceptions import ImproperlyConfigured
def get_env_variable(var_name, default=None):
"""获取环境变量,如果不存在且没有默认值则抛出异常"""
value = os.getenv(var_name, default)
if value is None:
raise ImproperlyConfigured(f"环境变量 {var_name} 未设置")
return value
# 项目根目录
BASE_DIR = Path(__file__).resolve().parent.parent.parent
# 密钥管理
SECRET_KEY = get_env_variable('DJANGO_SECRET_KEY')
# 调试模式
DEBUG = False
# 允许的主机
ALLOWED_HOSTS = []
# 应用定义
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
# 第三方应用
'rest_framework',
'corsheaders',
'django_filters',
'django_celery_beat',
# 本地应用
'apps.core',
'apps.articles',
'apps.comments',
'apps.analytics',
]
# 中间件
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'corsheaders.middleware.CorsMiddleware', # CORS支持
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
# 自定义中间件
'apps.core.middleware.PerformanceMiddleware',
'apps.core.middleware.LoggingMiddleware',
]
config/settings/development.py(开发环境):
from .base import *
DEBUG = True
ALLOWED_HOSTS = ['localhost', '127.0.0.1', '0.0.0.0']
# 数据库配置 - SQLite(开发环境)
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
# 静态文件
STATICFILES_DIRS = [BASE_DIR / 'static']
# 邮件后端 - 控制台输出
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
# CORS设置
CORS_ALLOW_ALL_ORIGINS = True
第二部分:Django高级特性实战解析
2.1 自定义用户模型(企业级认证体系)
Django默认的User模型在企业级应用中往往不够用。我们需要扩展用户功能。
apps/core/models.py:
from django.db import models
from django.contrib.auth.models import AbstractUser, BaseUserManager
from django.utils.translation import gettext_lazy as _
from django.core.validators import RegexValidator
class CustomUserManager(BaseUserManager):
"""自定义用户管理器"""
def create_user(self, email, password=None, **extra_fields):
"""创建普通用户"""
if not email:
raise ValueError('Email必须提供')
email = self.normalize_email(email)
user = self.model(email=email, **extra_fields)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, email, password=None, **extra_fields):
"""创建超级用户"""
extra_fields.setdefault('is_staff', True)
extra_fields.setdefault('is_superuser', True)
extra_fields.setdefault('is_active', True)
if extra_fields.get('is_staff') is not True:
raise ValueError('超级用户必须设置is_staff=True')
if extra_fields.get('is_superuser') is not True:
raise ValueError('超级用户必须设置is_superuser=True')
return self.create_user(email, password, **extra_fields)
class User(AbstractUser):
"""自定义用户模型"""
# 使用邮箱作为唯一标识,替代username
username = None
email = models.EmailField(_('邮箱地址'), unique=True)
# 用户基本信息扩展
phone = models.CharField(
_('手机号'),
max_length=11,
validators=[
RegexValidator(
regex=r'^1[3-9]\d{9}$',
message="手机号必须是11位数字,且以13-19开头"
)
],
blank=True,
null=True
)
# 企业级用户属性
department = models.CharField(_('部门'), max_length=100, blank=True)
position = models.CharField(_('职位'), max_length=100, blank=True)
employee_id = models.CharField(_('工号'), max_length=50, unique=True, blank=True, null=True)
# 权限相关
is_verified = models.BooleanField(_('已验证'), default=False)
verification_token = models.CharField(_('验证令牌'), max_length=100, blank=True)
verification_token_expires = models.DateTimeField(_('令牌过期时间'), blank=True, null=True)
# 时间戳
created_at = models.DateTimeField(_('创建时间'), auto_now_add=True)
updated_at = models.DateTimeField(_('更新时间'), auto_now=True)
# 设置认证字段为email
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
# 使用自定义管理器
objects = CustomUserManager()
class Meta:
verbose_name = _('用户')
verbose_name_plural = _('用户')
db_table = 'core_users'
ordering = ['-created_at']
indexes = [
models.Index(fields=['email']),
models.Index(fields=['phone']),
models.Index(fields=['employee_id']),
]
def __str__(self):
return self.email
def get_full_name(self):
"""获取用户全名"""
if self.first_name and self.last_name:
return f"{self.last_name}{self.first_name}"
return self.email
@property
def is_administrator(self):
"""判断是否为管理员"""
return self.is_staff or self.is_superuser
配置使用自定义用户模型:
# config/settings/base.py
AUTH_USER_MODEL = 'core.User'
2.2 中间件深度应用
中间件是Django请求/响应处理的钩子框架,企业级项目中常用于日志记录、性能监控、权限校验等。
apps/core/middleware.py:
import time
import logging
import json
from django.utils.deprecation import MiddlewareMixin
from django.db import connection
from django.core.cache import cache
from django.http import JsonResponse
logger = logging.getLogger('django')
class PerformanceMiddleware(MiddlewareMixin):
"""性能监控中间件"""
def process_request(self, request):
request._start_time = time.time()
request._query_count = 0
request._query_time = 0
def process_response(self, request, response):
# 计算总耗时
total_time = time.time() - request._start_time
# 收集性能指标
performance_data = {
'path': request.path,
'method': request.method,
'total_time': round(total_time * 1000, 2), # 毫秒
'query_count': len(connection.queries),
'query_time': sum(float(q['time']) for q in connection.queries) * 1000,
'status_code': response.status_code,
}
# 记录慢请求(超过500ms)
if total_time > 0.5:
logger.warning(f"慢请求检测: {json.dumps(performance_data)}")
# 添加性能头部信息(仅开发环境)
if settings.DEBUG:
response['X-Performance-TotalTime'] = f"{performance_data['total_time']}ms"
response['X-Performance-QueryCount'] = performance_data['query_count']
return response
class LoggingMiddleware(MiddlewareMixin):
"""请求日志中间件"""
def process_request(self, request):
# 记录请求开始
request_info = {
'method': request.method,
'path': request.path,
'ip': self._get_client_ip(request),
'user_agent': request.META.get('HTTP_USER_AGENT', ''),
'user': str(request.user) if request.user.is_authenticated else 'anonymous',
}
logger.info(f"请求开始: {json.dumps(request_info)}")
def process_response(self, request, response):
# 记录请求完成
logger.info(f"请求完成: {request.method} {request.path} - 状态码: {response.status_code}")
return response
def _get_client_ip(self, request):
"""获取客户端IP地址"""
x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
if x_forwarded_for:
ip = x_forwarded_for.split(',')[0]
else:
ip = request.META.get('REMOTE_ADDR')
return ip
class RateLimitMiddleware(MiddlewareMixin):
"""API限流中间件"""
def __init__(self, get_response):
super().__init__(get_response)
self.limit = 100 # 每分钟100次
self.window = 60 # 60秒窗口
def process_request(self, request):
# 仅对API路径限流
if not request.path.startswith('/api/'):
return None
# 获取客户端标识
client_id = self._get_client_id(request)
cache_key = f"rate_limit:{client_id}"
# 获取当前计数
current = cache.get(cache_key, 0)
if current >= self.limit:
# 超出限制,返回429
return JsonResponse({
'error': '请求过于频繁,请稍后再试',
'retry_after': self.window
}, status=429)
# 增加计数(如果不存在则创建,过期时间为窗口时间)
cache.set(cache_key, current + 1, self.window)
return None
def _get_client_id(self, request):
"""获取客户端唯一标识"""
if request.user.is_authenticated:
return f"user_{request.user.id}"
else:
ip = request.META.get('REMOTE_ADDR', 'unknown')
return f"ip_{ip}"
2.3 信号系统实战
信号允许特定发送者在特定动作发生时通知一组接收者,实现松耦合的组件通信。
apps/core/signals.py:
from django.db.models.signals import post_save, post_delete, pre_save
from django.dispatch import receiver
from django.contrib.auth import get_user_model
from django.core.cache import cache
from django.utils import timezone
from django.core.mail import send_mail
from django.conf import settings
from django.db import transaction
from apps.core.models import User
User = get_user_model()
@receiver(post_save, sender=User)
def user_post_save_handler(sender, instance, created, **kwargs):
"""用户保存后的信号处理"""
if created:
# 新用户注册,发送欢迎邮件
send_welcome_email.delay(instance.id)
# 更新用户统计
update_user_statistics.delay()
# 记录用户注册日志
logger.info(f"新用户注册: {instance.email}")
# 清理用户相关的缓存
cache.delete_pattern(f"user:{instance.id}:*")
cache.delete_pattern(f"profile:{instance.id}:*")
@receiver(pre_save, sender=User)
def user_pre_save_handler(sender, instance, **kwargs):
"""用户保存前的信号处理"""
# 邮箱变更时重置验证状态
if instance.pk:
try:
old_instance = User.objects.get(pk=instance.pk)
if old_instance.email != instance.email:
instance.is_verified = False
logger.info(f"用户邮箱变更: {old_instance.email} -> {instance.email}")
except User.DoesNotExist:
pass
@receiver(post_delete, sender=User)
def user_post_delete_handler(sender, instance, **kwargs):
"""用户删除后的信号处理"""
# 清理相关数据
cleanup_user_data.delay(instance.id)
# 记录删除日志
logger.info(f"用户删除: {instance.email}")
# 文章相关信号
@receiver(post_save, sender='apps.articles.Article')
def article_post_save_handler(sender, instance, created, **kwargs):
"""文章保存后的信号处理"""
if created:
# 新文章发布,通知关注者
notify_followers.delay(instance.id, 'new_article')
# 更新文章缓存
update_article_cache.delay(instance.id)
# 更新相关统计
update_article_statistics.delay(instance.id)
@receiver(post_delete, sender='apps.articles.Article')
def article_post_delete_handler(sender, instance, **kwargs):
"""文章删除后的信号处理"""
# 清理文章相关缓存
cache.delete_pattern(f"article:{instance.id}:*")
cache.delete_pattern(f"author:{instance.author_id}:articles:*")
# 评论相关信号
@receiver(post_save, sender='apps.comments.Comment')
def comment_post_save_handler(sender, instance, created, **kwargs):
"""评论保存后的信号处理"""
if created:
# 新评论,通知文章作者
notify_article_author.delay(instance.id)
# 更新评论计数
update_comment_count.delay(instance.article_id)
# 敏感词检测
check_sensitive_words.delay(instance.id)
apps/core/tasks.py(Celery任务):
from celery import shared_task
from django.core.mail import send_mail
from django.conf import settings
from django.core.cache import cache
import logging
logger = logging.getLogger(__name__)
@shared_task
def send_welcome_email(user_id):
"""发送欢迎邮件"""
from apps.core.models import User
try:
user = User.objects.get(id=user_id)
subject = "欢迎加入我们的平台!"
message = f"""
尊敬的{user.get_full_name()}:
欢迎您注册我们的平台!
您的账号已经创建成功,以下是您的账号信息:
- 邮箱:{user.email}
- 注册时间:{user.created_at}
请及时验证您的邮箱地址,以享受完整的功能。
如果您有任何问题,请随时联系我们。
谢谢!
"""
send_mail(
subject,
message,
settings.DEFAULT_FROM_EMAIL,
[user.email],
fail_silently=False,
)
logger.info(f"欢迎邮件已发送给用户: {user.email}")
except User.DoesNotExist:
logger.error(f"发送欢迎邮件失败,用户不存在: {user_id}")
@shared_task
def update_user_statistics():
"""更新用户统计"""
from apps.core.models import User
from django.utils import timezone
# 统计用户总数
total_users = User.objects.count()
# 统计今日新增用户
today_start = timezone.now().replace(hour=0, minute=0, second=0, microsecond=0)
today_users = User.objects.filter(created_at__gte=today_start).count()
# 更新缓存
cache.set('stats:total_users', total_users, 300) # 缓存5分钟
cache.set('stats:today_users', today_users, 300)
logger.info(f"用户统计已更新: 总数={total_users}, 今日新增={today_users}")
@shared_task
def cleanup_user_data(user_id):
"""清理用户相关数据"""
# 这里可以清理用户的文章、评论、文件等关联数据
logger.info(f"用户数据清理完成: {user_id}")
2.4 ORM高级查询与性能优化
Django ORM提供了强大的查询能力,但需要正确使用才能发挥性能优势。
apps/articles/models.py:
from django.db import models
from django.contrib.auth import get_user_model
from django.utils.translation import gettext_lazy as _
from django.db.models import Q, F, Count, Avg, Max, Min, Sum, Case, When, Value
from django.db.models.functions import Coalesce, TruncDate
from django.core.cache import cache
import uuid
User = get_user_model()
class Category(models.Model):
"""文章分类"""
name = models.CharField(_('分类名称'), max_length=100, unique=True)
slug = models.SlugField(_('URL标识'), max_length=100, unique=True)
description = models.TextField(_('描述'), blank=True)
order = models.IntegerField(_('排序'), default=0)
is_active = models.BooleanField(_('是否激活'), default=True)
class Meta:
verbose_name = _('分类')
verbose_name_plural = _('分类')
ordering = ['order', 'name']
def __str__(self):
return self.name
class Tag(models.Model):
"""文章标签"""
name = models.CharField(_('标签名称'), max_length=50, unique=True)
slug = models.SlugField(_('URL标识'), max_length=50, unique=True)
class Meta:
verbose_name = _('标签')
verbose_name_plural = _('标签')
def __str__(self):
return self.name
class ArticleManager(models.Manager):
"""文章管理器"""
def get_queryset(self):
"""默认查询集 - 只返回已发布文章"""
return super().get_queryset().filter(status='published')
def with_related_data(self):
"""预加载相关数据"""
return self.get_queryset().select_related(
'author', 'category'
).prefetch_related(
'tags', 'comments'
)
def popular(self, days=30):
"""获取近期热门文章"""
from django.utils import timezone
from django.db.models import Count
date_threshold = timezone.now() - timezone.timedelta(days=days)
return self.filter(
status='published',
published_at__gte=date_threshold
).annotate(
view_count=Count('views', distinct=True),
comment_count=Count('comments', distinct=True),
like_count=Count('likes', distinct=True),
).order_by('-view_count', '-like_count')
def search(self, query):
"""全文搜索"""
if not query:
return self.none()
search_terms = query.split()
q_objects = Q()
for term in search_terms:
q_objects |= Q(title__icontains=term) | Q(content__icontains=term)
return self.filter(q_objects).distinct()
def statistics(self):
"""获取文章统计信息"""
return self.aggregate(
total_articles=Count('id'),
total_views=Sum('view_count'),
avg_views=Avg('view_count'),
max_views=Max('view_count'),
min_views=Min('view_count'),
today_articles=Count('id', filter=Q(published_at__date=timezone.now().date())),
)
class Article(models.Model):
"""文章模型"""
STATUS_CHOICES = [
('draft', _('草稿')),
('review', _('审核中')),
('published', _('已发布')),
('archived', _('已归档')),
]
# 基本信息
title = models.CharField(_('标题'), max_length=200)
slug = models.SlugField(_('URL标识'), max_length=200, unique=True)
excerpt = models.TextField(_('摘要'), blank=True)
content = models.TextField(_('内容'))
# 关系字段
author = models.ForeignKey(
User,
on_delete=models.CASCADE,
related_name='articles',
verbose_name=_('作者')
)
category = models.ForeignKey(
Category,
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name='articles',
verbose_name=_('分类')
)
tags = models.ManyToManyField(
Tag,
blank=True,
related_name='articles',
verbose_name=_('标签')
)
# 状态与时间
status = models.CharField(
_('状态'),
max_length=20,
choices=STATUS_CHOICES,
default='draft'
)
published_at = models.DateTimeField(_('发布时间'), null=True, blank=True)
created_at = models.DateTimeField(_('创建时间'), auto_now_add=True)
updated_at = models.DateTimeField(_('更新时间'), auto_now=True)
# 统计字段
view_count = models.IntegerField(_('浏览数'), default=0)
like_count = models.IntegerField(_('点赞数'), default=0)
comment_count = models.IntegerField(_('评论数'), default=0)
# 元数据
meta_title = models.CharField(_('SEO标题'), max_length=200, blank=True)
meta_description = models.TextField(_('SEO描述'), blank=True)
meta_keywords = models.CharField(_('SEO关键词'), max_length=255, blank=True)
# 是否置顶
is_sticky = models.BooleanField(_('置顶'), default=False)
# 使用自定义管理器
objects = ArticleManager()
all_objects = models.Manager() # 用于查看所有文章(包括非发布状态)
class Meta:
verbose_name = _('文章')
verbose_name_plural = _('文章')
ordering = ['-published_at', '-created_at']
indexes = [
models.Index(fields=['status', 'published_at']),
models.Index(fields=['author', 'status']),
models.Index(fields=['category', 'status']),
models.Index(fields=['slug']),
]
constraints = [
models.CheckConstraint(
check=Q(status__in=['draft', 'review', 'published', 'archived']),
name='valid_article_status'
),
]
def __str__(self):
return self.title
def save(self, *args, **kwargs):
"""自定义保存逻辑"""
# 如果状态变为published且published_at为空,设置发布时间
if self.status == 'published' and not self.published_at:
self.published_at = timezone.now()
# 更新统计字段
if self.pk:
self.comment_count = self.comments.count()
super().save(*args, **kwargs)
# 更新缓存
self.update_cache()
def update_cache(self):
"""更新文章缓存"""
cache_key = f"article:{self.id}:detail"
cache_data = {
'id': self.id,
'title': self.title,
'slug': self.slug,
'excerpt': self.excerpt,
'author': self.author.email,
'category': self.category.name if self.category else None,
'published_at': self.published_at.isoformat() if self.published_at else None,
'view_count': self.view_count,
'like_count': self.like_count,
}
cache.set(cache_key, cache_data, 3600) # 缓存1小时
def increment_view_count(self):
"""增加浏览计数"""
# 使用F表达式避免竞态条件
Article.objects.filter(id=self.id).update(
view_count=F('view_count') + 1
)
# 刷新实例
self.refresh_from_db()
@property
def reading_time(self):
"""计算阅读时间(每分钟约300字)"""
word_count = len(self.content.split())
minutes = max(1, word_count // 300)
return f"{minutes}分钟"
@classmethod
def get_popular_articles_cached(cls, days=30, limit=10):
"""获取缓存的流行文章"""
cache_key = f"articles:popular:{days}:{limit}"
articles = cache.get(cache_key)
if not articles:
articles = list(cls.objects.popular(days)[:limit])
cache.set(cache_key, articles, 300) # 缓存5分钟
return articles
ORM高级查询示例:
# 1. 复杂条件查询(Q对象)
from django.db.models import Q
# 查找标题包含"Django"或内容包含"高级特性"的文章
articles = Article.objects.filter(
Q(title__icontains='Django') | Q(content__icontains='高级特性')
)
# 查找作者为1且状态为published,或者分类为2的文章
articles = Article.objects.filter(
Q(author_id=1, status='published') | Q(category_id=2)
)
# 2. 字段表达式(F表达式)
from django.db.models import F
# 将点赞数加1
Article.objects.filter(id=1).update(like_count=F('like_count') + 1)
# 查询浏览数大于点赞数的文章
articles = Article.objects.filter(view_count__gt=F('like_count'))
# 3. 聚合与注解
from django.db.models import Count, Avg, Sum, Max, Min
# 统计每个作者的文章数量
from django.db.models import Count
authors_with_count = User.objects.annotate(
article_count=Count('articles'),
total_views=Sum('articles__view_count')
).filter(article_count__gt=0).order_by('-article_count')
# 4. 条件表达式
from django.db.models import Case, When, Value, IntegerField
# 为文章添加热度评分
articles = Article.objects.annotate(
popularity_score=Case(
When(view_count__gt=1000, then=Value(5)),
When(view_count__gt=500, then=Value(4)),
When(view_count__gt=100, then=Value(3)),
When(view_count__gt=10, then=Value(2)),
default=Value(1),
output_field=IntegerField()
)
).order_by('-popularity_score', '-published_at')
# 5. 子查询
from django.db.models import Subquery, OuterRef
# 获取最新评论的文章
latest_comments = Comment.objects.filter(
article_id=OuterRef('id')
).order_by('-created_at').values('content')[:1]
articles = Article.objects.annotate(
latest_comment_content=Subquery(latest_comments)
)
# 6. 窗口函数(Django 4.0+)
from django.db.models import Window, F
from django.db.models.functions import Rank
# 为每个作者的文章按浏览数排名
articles = Article.objects.annotate(
rank=Window(
expression=Rank(),
partition_by=[F('author_id')],
order_by=F('view_count').desc()
)
)
2.5 数据库事务管理
企业级应用必须保证数据一致性,Django提供了强大的事务支持。
事务使用示例:
from django.db import transaction
from django.db.models import F
from django.core.exceptions import ValidationError
class OrderService:
"""订单服务类"""
@transaction.atomic
def create_order(self, user_id, product_id, quantity):
"""创建订单(完整事务)"""
from apps.orders.models import Order, OrderItem
from apps.products.models import Product
try:
# 获取产品并检查库存
product = Product.objects.select_for_update().get(id=product_id)
if product.stock < quantity:
raise ValidationError(f"库存不足,当前库存: {product.stock}")
# 创建订单
order = Order.objects.create(
user_id=user_id,
total_amount=product.price * quantity
)
# 创建订单项
OrderItem.objects.create(
order=order,
product=product,
quantity=quantity,
price=product.price
)
# 扣减库存
product.stock = F('stock') - quantity
product.save()
# 更新产品销量
product.sales = F('sales') + quantity
product.save()
return order
except Exception as e:
# 事务会自动回滚
logger.error(f"创建订单失败: {e}")
raise
@transaction.atomic
def bulk_create_orders(self, order_data_list):
"""批量创建订单"""
# 使用保存点实现部分回滚
sid = transaction.savepoint()
try:
orders = []
for data in order_data_list:
order = self._create_single_order(data)
orders.append(order)
# 提交所有订单
Order.objects.bulk_create(orders)
transaction.savepoint_commit(sid)
return orders
except Exception as e:
# 回滚到保存点
transaction.savepoint_rollback(sid)
logger.error(f"批量创建订单失败: {e}")
raise
def _create_single_order(self, data):
"""创建单个订单(内部方法)"""
# 实现逻辑...
pass
# 嵌套事务示例
@transaction.atomic
def process_user_registration(user_data, profile_data):
"""处理用户注册(嵌套事务)"""
# 创建用户(独立事务)
user = create_user(user_data)
try:
# 创建用户资料(可能失败)
profile = create_user_profile(user.id, profile_data)
# 发送欢迎邮件(异步,不影响主事务)
send_welcome_email.delay(user.id)
return user, profile
except Exception as e:
# 用户资料创建失败,但用户已创建
# 可以选择回滚整个事务,或者只处理异常
logger.error(f"用户资料创建失败,但用户已创建: {e}")
# 如果希望整个回滚,可以抛出异常
raise
@transaction.atomic
def create_user(user_data):
"""创建用户(独立事务)"""
# 实现逻辑...
pass
@transaction.atomic
def create_user_profile(user_id, profile_data):
"""创建用户资料(独立事务)"""
# 实现逻辑...
pass
2.6 缓存策略全方位实施
企业级应用必须合理使用缓存来提升性能。Django提供了强大的缓存框架。
缓存配置示例:
# config/settings/production.py
CACHES = {
'default': {
'BACKEND': 'django_redis.cache.RedisCache',
'LOCATION': f"redis://{env('REDIS_HOST')}:{env('REDIS_PORT')}/0",
'OPTIONS': {
'CLIENT_CLASS': 'django_redis.client.DefaultClient',
'CONNECTION_POOL_KWARGS': {
'max_connections': 100,
'retry_on_timeout': True,
},
'COMPRESSOR': 'django_redis.compressors.zlib.ZlibCompressor',
'IGNORE_EXCEPTIONS': True, # 缓存宕机不影响主流程
},
'KEY_PREFIX': 'cms',
'VERSION': 1,
'TIMEOUT': 3600, # 默认1小时
},
'session': {
'BACKEND': 'django_redis.cache.RedisCache',
'LOCATION': f"redis://{env('REDIS_HOST')}:{env('REDIS_PORT')}/1",
'OPTIONS': {
'CLIENT_CLASS': 'django_redis.client.DefaultClient',
}
},
'page': {
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
'TIMEOUT': 300, # 5分钟
'LOCATION': 'unique-snowflake',
}
}
# Session使用缓存
SESSION_ENGINE = 'django.contrib.sessions.backends.cache'
SESSION_CACHE_ALIAS = 'session'
# 缓存中间件
MIDDLEWARE = [
# ... 其他中间件
'django.middleware.cache.UpdateCacheMiddleware', # 必须第一个
'django.middleware.common.CommonMiddleware',
'django.middleware.cache.FetchFromCacheMiddleware', # 必须最后一个
]
# 缓存设置
CACHE_MIDDLEWARE_SECONDS = 60 * 15 # 15分钟
CACHE_MIDDLEWARE_KEY_PREFIX = 'cms'
CACHE_MIDDLEWARE_ALIAS = 'default'
缓存使用示例:
from django.core.cache import cache
from django.views.decorators.cache import cache_page
from django.utils.decorators import method_decorator
from rest_framework.views import APIView
import hashlib
import json
# 1. 视图级缓存
@cache_page(60 * 15) # 缓存15分钟
def article_list(request):
"""文章列表视图(自动缓存)"""
articles = Article.objects.with_related_data()[:20]
return render(request, 'articles/list.html', {'articles': articles})
# 2. 类视图缓存
class ArticleListView(APIView):
@method_decorator(cache_page(60 * 15))
def get(self, request):
"""文章列表API"""
articles = Article.objects.with_related_data()[:20]
serializer = ArticleSerializer(articles, many=True)
return Response(serializer.data)
# 3. 模板片段缓存
# 在模板中使用:{% load cache %}
# {% cache 600 "article_detail" article.id %}
# ... 内容 ...
# {% endcache %}
# 4. 低级缓存API
class ArticleService:
"""文章服务类"""
def get_article_detail(self, article_id):
"""获取文章详情(带缓存)"""
cache_key = f"article:{article_id}:detail"
# 尝试从缓存获取
cached_data = cache.get(cache_key)
if cached_data:
return cached_data
# 缓存未命中,查询数据库
article = Article.objects.with_related_data().get(id=article_id)
# 序列化数据
article_data = {
'id': article.id,
'title': article.title,
'content': article.content,
'author': article.author.email,
'published_at': article.published_at.isoformat() if article.published_at else None,
'view_count': article.view_count,
}
# 存入缓存(1小时)
cache.set(cache_key, article_data, 3600)
return article_data
def get_popular_articles(self, days=30, limit=10):
"""获取流行文章(带缓存)"""
cache_key = f"articles:popular:{days}:{limit}"
# 尝试从缓存获取
cached_data = cache.get(cache_key)
if cached_data:
return cached_data
# 缓存未命中,查询数据库
articles = Article.objects.popular(days)[:limit]
# 序列化数据
articles_data = [
{
'id': article.id,
'title': article.title,
'excerpt': article.excerpt,
'view_count': article.view_count,
'published_at': article.published_at.isoformat() if article.published_at else None,
}
for article in articles
]
# 存入缓存(5分钟)
cache.set(cache_key, articles_data, 300)
return articles_data
def invalidate_article_cache(self, article_id):
"""使文章缓存失效"""
# 删除文章详情缓存
cache.delete(f"article:{article_id}:detail")
# 删除相关列表缓存(可能需要批量删除)
cache.delete_pattern(f"articles:*:{article_id}:*")
# 删除包含该文章的列表缓存
cache.delete_pattern(f"articles:list:*")
cache.delete_pattern(f"articles:popular:*")
# 5. 缓存键生成器
def generate_cache_key(prefix, *args, **kwargs):
"""生成缓存键"""
# 将参数转换为字符串
arg_str = ':'.join(str(arg) for arg in args)
kwarg_str = ':'.join(f"{k}:{v}" for k, v in sorted(kwargs.items()))
# 生成MD5哈希(避免键过长)
hash_input = f"{prefix}:{arg_str}:{kwarg_str}"
hash_value = hashlib.md5(hash_input.encode()).hexdigest()
return f"{prefix}:{hash_value}"
# 使用示例
cache_key = generate_cache_key('article_list', page=1, page_size=20, category='python')
2.7 信号系统高级用法
信号系统是Django中实现组件解耦的利器,在企业级项目中有着广泛应用。让我们深入探讨信号的高级用法。
自定义信号创建与使用
Django不仅提供了内置信号,还允许我们创建自定义信号,实现更复杂的业务逻辑解耦。
# apps/core/signals.py
from django.dispatch import Signal, receiver
# 创建自定义信号
article_published = Signal()
article_review_requested = Signal()
user_profile_updated = Signal()
# 信号接收器
@receiver(article_published)
def handle_article_published(sender, article, user, **kwargs):
"""处理文章发布信号"""
from .tasks import (
notify_followers,
update_search_index,
generate_social_preview
)
# 异步任务处理
notify_followers.delay(article.id, user.id)
update_search_index.delay(article.id)
generate_social_preview.delay(article.id)
# 记录日志
logger.info(f"Article published: {article.title} by {user.email}")
@receiver(user_profile_updated)
def handle_user_profile_updated(sender, user, old_data, new_data, **kwargs):
"""处理用户资料更新信号"""
from .tasks import (
sync_user_to_third_party,
update_user_statistics,
send_profile_update_notification
)
# 异步同步到第三方系统
sync_user_to_third_party.delay(user.id)
# 更新统计
update_user_statistics.delay()
# 发送通知
if old_data.get('email') != new_data.get('email'):
send_profile_update_notification.delay(user.id, 'email_changed')
# 在业务代码中发送信号
from django.db import transaction
from .signals import article_published
@transaction.atomic
def publish_article(article_id, user_id):
"""发布文章(包含信号发送)"""
from .models import Article, User
article = Article.objects.get(id=article_id)
user = User.objects.get(id=user_id)
# 更新文章状态
article.status = 'published'
article.published_at = timezone.now()
article.save()
# 发送信号
article_published.send(
sender=Article,
article=article,
user=user
)
return article
信号与事务的协同工作
信号处理需要特别注意与数据库事务的协同,避免产生不一致状态。
# apps/core/signals.py
from django.db import transaction
from django.db.models.signals import post_save
from django.dispatch import receiver
@receiver(post_save, sender='apps.orders.Order')
def handle_order_created(sender, instance, created, **kwargs):
"""处理订单创建信号"""
if created:
# 使用事务确保数据一致性
with transaction.atomic():
# 更新库存
update_inventory(instance)
# 生成发货单
create_shipment(instance)
# 发送通知
send_order_confirmation(instance)
# 异步任务(在事务外执行)
process_order_async.delay(instance.id)
# 记录审计日志
log_order_creation(instance)
信号的性能优化
信号处理可能影响性能,需要合理优化:
# apps/core/signals.py
from django.dispatch import receiver
from django.core.cache import cache
import time
@receiver(post_save, sender='apps.articles.Article')
def handle_article_save(sender, instance, **kwargs):
"""文章保存信号处理(带性能优化)"""
# 防重入机制(避免递归调用)
cache_key = f'article_save_lock:{instance.id}'
if cache.get(cache_key):
return
try:
# 设置锁(5秒过期)
cache.set(cache_key, True, 5)
# 批量处理(避免频繁数据库操作)
if hasattr(instance, '_pending_updates'):
process_batch_updates(instance._pending_updates)
# 异步处理耗时操作
if instance.status == 'published':
update_related_caches.delay(instance.id)
finally:
# 清理锁
cache.delete(cache_key)
# 批量信号处理优化
class SignalBatchProcessor:
"""信号批量处理器"""
def __init__(self, signal, handler):
self.signal = signal
self.handler = handler
self.batch = []
self.batch_size = 100
def connect(self):
"""连接信号"""
self.signal.connect(self._batch_handler)
def _batch_handler(self, **kwargs):
"""批量处理信号"""
self.batch.append(kwargs)
if len(self.batch) >= self.batch_size:
self.flush()
def flush(self):
"""处理批量信号"""
if self.batch:
self.handler(self.batch)
self.batch.clear()
信号的测试策略
信号处理需要专门的测试策略,确保可靠性:
# tests/test_signals.py
from django.test import TestCase
from django.db.models.signals import post_save
from unittest.mock import patch, MagicMock
import apps.core.signals
class SignalTests(TestCase):
"""信号测试"""
def setUp(self):
"""测试准备"""
self.signal_handler = MagicMock()
def test_article_published_signal(self):
"""测试文章发布信号"""
from apps.core.signals import article_published
# 连接测试处理器
article_published.connect(self.signal_handler)
try:
# 创建测试数据
user = User.objects.create(email='test@example.com')
article = Article.objects.create(
title='Test Article',
author=user,
status='draft'
)
# 发布文章(会触发信号)
article.status = 'published'
article.save()
# 验证信号被调用
self.signal_handler.assert_called_once()
# 验证调用参数
call_args = self.signal_handler.call_args[1]
self.assertEqual(call_args['article'], article)
self.assertEqual(call_args['user'], user)
finally:
# 断开测试处理器
article_published.disconnect(self.signal_handler)
def test_signal_transaction_safety(self):
"""测试信号的事务安全性"""
from django.db import transaction
with patch('apps.core.signals.handle_order_created') as mock_handler:
# 模拟事务回滚场景
try:
with transaction.atomic():
# 创建订单
order = Order.objects.create(
user=self.user,
total_amount=100.00
)
# 故意抛出异常,触发事务回滚
raise Exception('Test transaction rollback')
except Exception:
pass
# 验证信号处理器未被调用(因为事务回滚)
mock_handler.assert_not_called()
信号的合理使用可以让代码更加清晰、可维护,是Django企业级开发的重要技能。
2.8 Admin后台深度定制
Django Admin是企业级应用中非常强大的后台管理系统,通过深度定制可以满足复杂业务需求。
apps/articles/admin.py:
from django.contrib import admin
from django.utils.html import format_html
from django.urls import reverse
from django.utils.safestring import mark_safe
from django.contrib.admin.models import LogEntry, ADDITION, CHANGE, DELETION
from django.contrib.contenttypes.models import ContentType
from django.db.models import Count, Avg, Sum
import json
from .models import Article, Category, Tag
class CategoryAdmin(admin.ModelAdmin):
"""分类管理"""
list_display = ['name', 'slug', 'article_count', 'order', 'is_active']
list_editable = ['order', 'is_active']
search_fields = ['name']
prepopulated_fields = {'slug': ['name']}
def article_count(self, obj):
"""文章数量"""
return obj.articles.count()
article_count.short_description = '文章数'
class TagAdmin(admin.ModelAdmin):
"""标签管理"""
list_display = ['name', 'slug', 'article_count']
search_fields = ['name']
prepopulated_fields = {'slug': ['name']}
def article_count(self, obj):
"""文章数量"""
return obj.articles.count()
article_count.short_description = '文章数'
class ArticleAdmin(admin.ModelAdmin):
"""文章管理"""
# 列表页配置
list_display = [
'title', 'author', 'category', 'status',
'view_count', 'like_count', 'comment_count',
'published_at', 'is_sticky', 'actions'
]
list_filter = [
'status', 'category', 'author', 'published_at',
'is_sticky', 'created_at'
]
search_fields = ['title', 'content', 'author__email']
list_editable = ['status', 'is_sticky']
list_per_page = 20
# 详情页配置
fieldsets = [
('基本信息', {
'fields': [
'title', 'slug', 'excerpt', 'content',
'author', 'category', 'tags'
]
}),
('状态设置', {
'fields': [
'status', 'published_at', 'is_sticky'
],
'classes': ['collapse']
}),
('SEO设置', {
'fields': [
'meta_title', 'meta_description', 'meta_keywords'
],
'classes': ['collapse']
}),
('统计信息', {
'fields': [
'view_count', 'like_count', 'comment_count'
],
'classes': ['collapse']
}),
]
# 自动完成字段
autocomplete_fields = ['author', 'category', 'tags']
# 预填充字段
prepopulated_fields = {'slug': ['title']}
# 只读字段
readonly_fields = ['created_at', 'updated_at']
# 自定义动作
actions = ['make_published', 'make_draft', 'sticky_articles', 'export_articles']
# 自定义方法
def actions(self, obj):
"""自定义操作列"""
if obj.status == 'draft':
return format_html(
'<a class="button" href="{}">预览</a> '
'<a class="button" href="{}">发布</a>',
reverse('article_preview', args=[obj.id]),
reverse('admin:articles_article_publish', args=[obj.id])
)
elif obj.status == 'published':
return format_html(
'<a class="button" href="{}" target="_blank">查看</a> '
'<a class="button" href="{}">取消发布</a>',
reverse('article_detail', args=[obj.slug]),
reverse('admin:articles_article_unpublish', args=[obj.id])
)
return '-'
actions.short_description = '操作'
# 自定义动作实现
def make_published(self, request, queryset):
"""批量发布文章"""
updated = queryset.update(status='published')
self.message_user(request, f"{updated}篇文章已发布")
make_published.short_description = "发布选中文章"
def make_draft(self, request, queryset):
"""批量设为草稿"""
updated = queryset.update(status='draft')
self.message_user(request, f"{updated}篇文章已设为草稿")
make_draft.short_description = "设为草稿"
def sticky_articles(self, request, queryset):
"""批量置顶/取消置顶"""
# 实现逻辑...
pass
def export_articles(self, request, queryset):
"""导出文章"""
# 实现逻辑...
pass
# 保存逻辑
def save_model(self, request, obj, form, change):
"""保存模型"""
if not change: # 新增
obj.author = request.user
super().save_model(request, obj, form, change)
# 记录日志
if change:
action = CHANGE
else:
action = ADDITION
LogEntry.objects.log_action(
user_id=request.user.id,
content_type_id=ContentType.objects.get_for_model(obj).pk,
object_id=obj.pk,
object_repr=str(obj),
action_flag=action,
change_message=json.dumps(form.cleaned_data)
)
# 自定义模板
change_list_template = 'admin/articles/article/change_list.html'
# 自定义JavaScript和CSS
class Media:
css = {
'all': ('css/admin/article.css',)
}
js = ('js/admin/article.js',)
# 注册模型
admin.site.register(Category, CategoryAdmin)
admin.site.register(Tag, TagAdmin)
admin.site.register(Article, ArticleAdmin)
自定义Admin视图:
from django.contrib.admin.views.decorators import staff_member_required
from django.shortcuts import render
from django.http import JsonResponse
from django.db.models import Count, Sum, Avg
from datetime import datetime, timedelta
from apps.articles.models import Article
from apps.core.models import User
@staff_member_required
def admin_dashboard(request):
"""自定义Admin仪表板"""
# 统计信息
total_articles = Article.all_objects.count()
published_articles = Article.objects.count()
total_users = User.objects.count()
today_users = User.objects.filter(
created_at__date=datetime.now().date()
).count()
# 最近7天文章发布趋势
date_threshold = datetime.now() - timedelta(days=7)
daily_stats = Article.all_objects.filter(
created_at__gte=date_threshold
).extra(
{'date': "date(created_at)"}
).values('date').annotate(
count=Count('id')
).order_by('date')
# 热门作者
popular_authors = User.objects.annotate(
article_count=Count('articles'),
total_views=Sum('articles__view_count')
).filter(article_count__gt=0).order_by('-total_views')[:10]
context = {
'total_articles': total_articles,
'published_articles': published_articles,
'total_users': total_users,
'today_users': today_users,
'daily_stats': list(daily_stats),
'popular_authors': popular_authors,
'title': '系统仪表板',
}
return render(request, 'admin/dashboard.html', context)
@staff_mrequired
def admin_statistics_api(request):
"""统计信息API"""

time_range = request.GET.get('range', '7d')
if time_range == '7d':
days = 7
elif time_range == '30d':
days = 30
elif time_range == '90d':
days = 90
else:
days = 7
date_threshold = datetime.now() - timedelta(days=days)
# 文章发布趋势
article_stats = Article.all_objects.filter(
created_at__gte=date_threshold
).extra(
{'date': "date(created_at)"}
).values('date').annotate(
count=Count('id')
).order_by('date')
# 用户注册趋势
user_stats = User.objects.filter(
created_at__gte=date_threshold
).extra(
{'date': "date(created_at)"}
).values('date').annotate(
count=Count('id')
).order_by('date')
# 热门文章
popular_articles = Article.objects.order_by('-view_count')[:10]
data = {
'article_stats': list(article_stats),
'user_stats': list(user_stats),
'popular_articles': [
{
'id': article.id,
'title': article.title,
'views': article.view_count,
'author': article.author.email,
}
for article in popular_articles
],
}
return JsonResponse(data)