Django 基础核心考点
在 Django 面试题 中,基础概念往往是考察的第一步。面试官通常希望了解候选人对 MTV 模式、WSGI 接口以及请求生命周期的理解深度。
1.1 WSGI 与 ASGI 的区别
WSGI (Web Server Gateway Interface) 是 Python Web 应用与 Web 服务器之间的标准接口。Django 传统上基于 WSGI,它是同步的,意味着每个请求必须等待前一个请求处理完毕才能开始。然而,随着 WebSocket 和异步编程的兴起,ASGI (Asynchronous Server Gateway Interface) 应运而生。ASGI 支持异步 I/O,允许 Django 处理长连接和并发请求,这在现代 Django 面试题 中越来越重要。
WSGI 特点
同步阻塞、标准接口、兼容性好、适合传统 CRUD 业务。
ASGI 特点
异步非阻塞、支持 WebSocket、高性能、适合实时通信场景。
1.2 Django 请求生命周期
理解请求从发送到响应的完整流程是 Django 面试题 中的高频考点。流程如下:
- 路由分发: URLconf 匹配 URL,找到对应的 View。
- 中间件处理: 请求经过请求中间件(如 CsrfViewMiddleware)。
- 视图执行: View 函数或类被调用,处理业务逻辑。
- ORM 操作: 如有数据库操作,执行 SQL 查询。
- 模板渲染: 数据传入模板,生成 HTML。
- 响应返回: 经过响应中间件,返回给客户端。
ORM 与数据库深度解析
ORM (Object-Relational Mapping) 是 Django 的核心组件之一。在 Django 面试题 中,关于 N+1 问题、查询优化和事务处理的问题层出不穷。
2.1 解决 N+1 查询问题
N+1 问题是 Django ORM 中最常见的性能陷阱。当查询一个主对象列表时,如果每个主对象都关联了一个外键,且没有预先加载,Django 会为每个主对象单独发起一次数据库查询。
books = Book.objects.all()
for book in books:
print(book.author.name) # 每次循环都执行一次 SQL,导致 N+1 问题
# 使用 select_related (针对 ForeignKey, OneToOneField)
books = Book.objects.select_related('author').all()
for book in books:
print(book.author.name) # 只有一次 SQL 查询,通过 JOIN 实现
# 使用 prefetch_related (针对 ManyToManyField, Reverse ForeignKey)
books = Book.objects.prefetch_related('tags').all()
for book in books:
print(list(book.tags.all()))
2.2 事务管理
在 Django 面试题 中,数据一致性至关重要。Django 提供了 `transaction.atomic()` 上下文管理器来确保操作的原子性。如果块内发生异常,所有更改将回滚。
from django.db import transaction
def transfer_money(user_from, user_to, amount):
with transaction.atomic():
user_from.balance -= amount
user_from.save()
user_to.balance += amount
user_to.save()
# 如果上面任何一步出错,整个事务回滚
中间件机制详解
中间件是 Django 请求/响应处理的钩子框架。它是一个轻量级、低级别的“插件”系统,用于全局修改 Django 的输入或输出。在 Django 面试题 中,自定义中间件是考察点之一。
3.1 中间件执行流程
请求到达服务器,依次经过 MIDDLEWARE 列表中定义的中间件的 process_request 方法。如果某个中间件返回了 HttpResponse,则后续中间件及 View 将不再执行,直接进入响应阶段。
View 函数被调用,执行业务逻辑,返回 HttpResponse 对象。
响应对象依次经过中间件的 process_response 方法(顺序与 request 相反)。最后返回给客户端。
3.2 自定义中间件示例
以下是一个简单的中间件,用于记录请求日志:
import logging
logger = logging.getLogger(__name__)
class RequestLogMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
# 请求前处理
logger.info(f"Request started: {request.path}")
response = self.get_response(request)
# 响应后处理
logger.info(f"Request finished: {request.path}, Status: {response.status_code}")
return response
缓存策略与性能优化
性能优化是 Django 面试题 中区分初级与高级开发者的关键。Django 提供了多级缓存机制,包括内存缓存、文件缓存、数据库缓存和 Memcached/Redis 缓存。
4.1 缓存后端选择
| 缓存后端 | 速度 | 持久性 | 适用场景 |
|---|---|---|---|
| LocMemCache | 极快 | 无 | 单进程开发环境 |
| FileBasedCache | 慢 | 有 | 无外部依赖的简单场景 |
| Memcached/Redis | 快 | 无 (可配置) | 生产环境高并发 |
4.2 缓存颗粒度
在 Django 面试题 中,面试官常问:“你应该缓存什么?”
- 页面缓存: 使用
@page_cache装饰器或中间件,缓存整个 HTML 响应。适合变化不频繁的内容。 - 视图缓存: 使用
@cache_page装饰器,缓存视图返回的 HttpResponse。适合动态生成但数据更新不频繁的页面。 - 模板片段缓存: 使用
{% load cache %}和{% cache %}标签,缓存模板中的特定部分。这是最灵活的策略。 - 数据缓存: 使用
cache.set()和cache.get()直接操作 Python 对象。适合复杂查询结果的重用。
高阶实战与源码剖析
对于资深职位的 Django 面试题,往往涉及源码级理解和高并发场景下的解决方案。
5.1 Django 信号量 (Signals) 的利弊
信号量提供了松耦合的组件间通信机制,如 post_save、pre_delete。然而,在 Django 面试题 中,许多面试官对信号量持保留态度,原因如下:
- 调试困难: 信号的处理逻辑分散在不同文件中,难以追踪。
- 性能开销: 在批量操作中,如果信号处理逻辑复杂,可能导致性能下降。
- 隐式依赖: 代码行为变得不直观,增加维护成本。
建议: 仅在确实需要解耦的场景下使用信号量,否则优先考虑直接在 View 或 Model 方法中处理逻辑。
5.2 异步任务处理
Django 本身是同步的,但在 Django 面试题 中,异步任务处理是必考题。通常结合 Celery + Redis/RabbitMQ 实现。Celery 将耗时任务(如发送邮件、图像处理)放入消息队列,由 Worker 异步执行,从而释放 Web 服务器的资源。
from celery import shared_task
@shared_task
def send_welcome_email(user_id):
user = User.objects.get(id=user_id)
# 发送逻辑
pass