python Web开发从入门到精通(十七)Django高级特性深度解析 - 掌握企业级开发技巧(上)

8 阅读1分钟

摘要钩子:学完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>&nbsp;'
                '<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>&nbsp;'
                '<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"""
    
    ![API接口响应示例](outputs/covers/API接口响应.jpg)
    
    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)