目录
- Django 基础
- ORM (对象关系映射)
- 视图、模板与 URL
- 中间件
- 表单
- 认证与授权
- 性能优化
- 高级特性
- 项目与部署
- 系统设计
- 开放性问题
Django 基础
1 什么是 Django?它的核心理念是什么?
参考回答: Django 是一个高级的 Python Web 框架,它鼓励快速开发和干净、实用的设计,它的核心理念是 "DRY" (Don't Repeat Yourself) 和 "BBAW" (Batteries Included)。

- DRY: 不要重复自己,鼓励代码复用。
- BBAW: 内置电池,提供了 Web 开发所需的大部分功能,如 ORM、路由、模板引擎、后台管理等,开发者不需要自己“重新造轮子”。
2 Django 的 MTV 架构是什么?它和 MVC 有什么区别?
参考回答: Django 遵循 MTV (Model-Template-View) 架构模式,这是一种 MVC (Model-View-Controller) 的变种。
- Model (模型): 负责与数据库交互,定义数据结构,对应 MVC 中的 Model。
- Template (模板): 负责展示,是 HTML 文件,嵌入 Python 代码来动态生成页面,对应 MVC 中的 View (用户看到的界面)。
- View (视图): 负责业务逻辑,接收请求,从 Model 获取数据,调用 Template 渲染页面,并返回响应,对应 MVC 中的 Controller (处理用户输入并调用 Model 和 View)。
区别:主要是术语上的不同,Django 的 View 更像是 MVC 中的 Controller,而 Django 的 Template 才是 MVC 中的 View。
3 描述一下 Django 的请求生命周期。
参考回答: 当一个请求到达 Django 应用时,它会经历以下步骤:
- WSGI 服务器: 请求首先由 WSGI 服务器(如 Gunicorn, uWSGI)接收。
- 加载配置: WSGI 服务器加载 Django 项目的
settings.py文件。 - URL 路由: Django 的
URL Dispatcher根据urls.py中的配置,匹配请求的 URL,找到对应的view函数或类。 - 中间件: 在调用
view之前,请求会依次通过所有已激活的Middleware的process_request方法,这些方法可以进行请求预处理、身份验证等。 - 视图: 找到的
view函数被调用,它负责处理业务逻辑,可能包括查询数据库、调用其他服务等。 - 模板渲染:
view返回一个HttpResponse,并且需要渲染模板,它会将数据传递给模板引擎,生成最终的 HTML。 - 响应:
view返回一个HttpResponse对象。 - 中间件: 在响应返回给客户端之前,它会依次通过所有
Middleware的process_response方法,可以进行响应后处理,如添加 CORS 头、压缩等。 - WSGI 服务器: WSGI 服务器将最终的 HTTP 响应发送给客户端。
4 Django 的 settings.py 文件中,必须包含哪些关键配置?
参考回答:
一些核心的 settings.py 配置包括:

DEBUG: 是否开启调试模式,生产环境必须设为False。SECRET_KEY: 用于加密 session、密码等,是安全的关键。INSTALLED_APPS: 列出项目中所有启用的 Django 应用。DATABASES: 数据库连接配置,如default数据库的类型、主机、端口、用户名、密码等。ROOT_URLCONF: 项目的根 URL 配置文件路径。MIDDLEWARE: 中间件的列表,决定了请求和响应的处理顺序。TEMPLATES: 模板引擎的配置,如模板目录、加载器等。STATIC_URL和STATIC_ROOT: 静态文件(CSS, JS, 图片)的 URL 前缀和收集路径。MEDIA_URL和MEDIA_ROOT: 用户上传文件的 URL 前缀和存储路径。
ORM (对象关系映射)
1 解释 Django ORM 中的 select_related 和 prefetch_related 的区别和使用场景。
参考回答: 两者都是为了解决“N+1 查询问题”(即循环 N 次数据库查询,每次查询关联对象),但实现方式不同。
-
select_related:- 原理: 使用 SQL 的
JOIN语句在查询时一次性获取主对象和其外键关联的对象。 - 适用场景: 主要用于处理 一对一 和 多对一 的外键关系,因为它使用 JOIN,所以对于深度嵌套的关系,可能会生成非常复杂的 SQL,甚至性能更差。
- 示例:
Post.objects.select_related('author').all()会在一条查询中获取所有文章及其作者信息。
- 原理: 使用 SQL 的
-
prefetch_related:- 原理: 执行两条(或多条)独立的查询,第一条查询获取所有主对象,第二条查询获取所有关联对象,然后在 Python 内存中将它们关联起来。
- 适用场景: 主要用于处理 多对多 关系,也适用于
select_related不适用或性能不佳的 多对一 关系,因为它不使用 JOIN,所以不会生成复杂的 SQL,对于深度嵌套关系更友好。 - 示例:
Post.objects.prefetch_related('tags').all()会先查询所有文章,再查询所有文章对应的标签,然后在内存中匹配。
优先使用 select_related(对于外键),如果遇到多对多关系或 select_related 效果不佳时,使用 prefetch_related。

2 什么是 QuerySet?它有什么特性?
参考回答:
QuerySet 是 Django ORM 中表示数据库对象的集合,它是一个惰性执行的“查询”对象,只有在真正需要数据时(如迭代、切片、调用 len() 或 list())才会向数据库发送查询。
主要特性:
- 惰性求值:
Post.objects.all()这行代码本身不会执行数据库查询。 - 链式调用: 可以连续调用过滤、排序等方法,如
Post.objects.filter(published=True).order_by('-pub_date')。 - 缓存: 对同一个
QuerySet进行多次数据访问,数据库查询只执行一次。q = Post.objects.all(),len(q)和list(q)都只会触发一次查询。 - 可切片: 可以使用 Python 的切片语法
[:]来获取一部分数据,但要注意切片后的QuerySet不会被缓存。
3 如何在 Django 中实现数据库事务?
参考回答: Django 提供了多种方式使用事务:
-
装饰器 (最常用):
from django.db import transaction @transaction.atomic def create_order(user, items): # 这些操作都在一个事务中 order = Order.objects.create(user=user) for item in items: OrderItem.objects.create(order=order, product=item.product, quantity=item.quantity) # 如果这里发生异常,整个事务会回滚 -
上下文管理器 (更灵活):
from django.db import transaction def create_order(user, items): with transaction.atomic(): order = Order.objects.create(user=user) # ... 其他操作 ... -
在视图中使用: 通过
transaction.atomic()装饰器整个视图函数,或者使用@transaction.atomic和savepoint进行更细粒度的控制。
视图、模板与 URL
1 解释类视图 和函数视图 的区别和优缺点。
参考回答:
-
函数视图:
- 优点: 简单直观,易于理解,适合逻辑简单的视图。
- 缺点: 当视图逻辑变复杂时,函数会变得臃肿且难以维护,代码复用性差。
-
类视图:
- 优点:
- 代码复用: 基于 OOP,可以通过继承和组合来复用代码。
- 逻辑分离: 使用不同的方法(如
get(),post())处理不同的 HTTP 方法,逻辑更清晰。 - 扩展性强: 可以轻松地使用 Mixin(混入类)来添加功能,如权限检查、登录重定向等。
- 内置功能: Django 提供了大量内置的通用类视图(如
ListView,DetailView),可以快速实现常见功能。
- 缺点: 对于初学者来说,理解成本较高,不如函数视图直观。
- 优点:
现代 Django 开发更推荐使用类视图,特别是对于有复杂逻辑或需要复用的视图。
2 什么是模板继承?如何使用 {% block %}
参考回答:
模板继承是 Django 模板引擎的一个强大功能,它允许你创建一个基础模板,然后在子模板中重用它的结构,只覆盖需要修改的部分。
-
{% block %}: 在基础模板中定义一个或多个 block,这些 block 是子模板可以填充的占位符。
<!-- base.html -->
<html>
<head>
<title>{% block title %}My Site{% endblock %}</title>
</head>
<body>
{% block content %}
{% endblock %}
</body>
</html>
-
{% extends %}: 在子模板中,使用 extends 来指定它继承自哪个基础模板。
<!-- article_detail.html -->
{% extends "base.html" %}
{% block title %}{{ article.title }}{% endblock %}
{% block content %}
<h1>{{ article.title }}</h1>
<p>{{ article.body }}</p>
{% endblock %}
3 Django 的 reverse 函数是做什么用的?
参考回答:
reverse 函数用于根据 URL 的名称(在 urls.py 中通过 name 参数定义)来动态生成 URL 的路径字符串,这对于代码的可维护性至关重要,因为如果 URL 路径改变,你只需要在 urls.py 中修改一处,而不需要在所有模板和视图中硬编码 URL。
示例:
-
在 urls.py 中定义一个带名称的 URL:
path('article/<int:pk>/', views.ArticleDetailView.as_view(), name='article_detail'),
-
在模板中使用:
<a href="{% url 'article_detail' pk=article.id %}">Read more</a>
-
在视图中使用:
from django.urls import reverse
def some_view(request):
url = reverse('article_detail', kwargs={'pk': 1})
# url 的值将是 '/article/1/'
中间件
1 什么是中间件?列举一个你使用过的中间件并说明其作用。
参考回答:
中间件是一个轻量级、低级别的“插件”系统,用于在全局范围内改变 Django 的输入或输出,它是一个 Python 类,位于请求和响应处理过程的中间,可以拦截并处理请求和响应。
常见的中间件:
django.middleware.security.SecurityMiddleware: 启用各种安全功能,如 X-Content-Type-Options, X-Frame-Options, Strict-Transport-Security (HSTS) 等,以及 HTTPS 重定向。
django.contrib.sessions.middleware.SessionMiddleware: 处理会话,使得用户登录状态等信息可以在多个请求间保持。
django.middleware.common.CommonMiddleware: 处理一些通用任务,如规范化请求字符集、处理 ETag 等。
- 自定义中间件: 一个用于记录每个请求耗时的中间件。
自定义中间件示例 (记录请求耗时):
import time
from django.utils.deprecation import MiddlewareMixin
class RequestTimeMiddleware(MiddlewareMixin):
def process_request(self, request):
request.start_time = time.time()
def process_response(self, request, response):
total_time = time.time() - request.start_time
print(f"Request to {request.path} took {total_time:.2f} seconds.")
return response
表单
1 Django 的 ModelForm 和普通 Form 有什么区别?
参考回答:
-
Form:
- 用于处理与模型无关的数据,如用户注册、联系表单等。
- 字段需要手动定义。
- 提供了数据验证和渲染 HTML 表单的功能。
-
ModelForm:
- 是
Form 的一个子类,与 Django 模型紧密集成。
- 可以通过
class Meta 中的 model 和 fields 自动从模型生成表单字段。
- 提供了
save() 方法,可以将表单数据直接保存或更新到数据库,非常方便。
- 适用于基于模型的 CRUD 操作。
示例:
# Model
from django.db import models
class Article(models.Model):= models.CharField(max_length=100)
content = models.TextField()
# ModelForm
from django.forms import ModelForm
class ArticleForm(ModelForm):
class Meta:
model = Article
fields = ['title', 'content'] # 自动生成 title 和 content 字段
# 使用
form = ArticleForm(request.POST)
if form.is_valid():
article = form.save() # 一行代码保存到数据库
认证与授权
1 如何实现一个自定义的用户模型?
参考回答:
从 Django 1.10 开始,官方推荐使用 AbstractUser 或 AbstractBaseUser 来创建自定义用户模型,而不是直接继承 User。
-
在 settings.py 中指定自定义用户模型:
AUTH_USER_MODEL = 'myapp.CustomUser'
-
创建自定义用户模型:
# myapp/models.py
from django.contrib.auth.models import AbstractUser
from django.db import models
class CustomUser(AbstractUser):
# 添加自定义字段
bio = models.TextField(blank=True)
birth_date = models.DateField(null=True, blank=True)
# 可以重写或添加方法
-
创建并应用迁移:
python manage.py makemigrations
python manage.py migrate
-
创建超级用户时,需要使用自定义模型的字段:
python manage.py createsuperuser --username=myuser --email=myuser@example.com
2 解释 Django 的权限系统是如何工作的?
参考回答:
Django 的权限系统基于 auth_permission 表,它提供了细粒度的控制。
-
自动创建权限: 对于每个模型,Django 默认会创建三个权限:add_<model_name>, change_<model_name>, delete_<model_name>。
-
权限分配:
- 用户权限: 可以直接分配给
User 模型的实例。
- 组权限: 可以创建
Group,将权限分配给组,然后将用户添加到组中,用户自动继承组的所有权限。
-
在模板中使用:
{% if perms.myapp.add_article %}
<a href="/create-article/">Create Article</a>
{% endif %}
-
在视图中使用:
from django.contrib.auth.mixins import PermissionRequiredMixin
class CreateArticleView(PermissionRequiredMixin, View):
permission_required = 'myapp.add_article'
# ...
性能优化
1 如何优化 Django 应用的性能?
参考回答:
性能优化可以从多个层面进行:
-
数据库层面:
- 索引: 为经常用于
WHERE, JOIN, ORDER BY 的字段添加数据库索引。
- 查询优化: 使用
select_related 和 prefetch_related 避免 N+1 查询,使用 only() 和 defer() 只查询需要的字段。
- 数据库连接池: 使用
django-db-connection-pool 或配置 WSGI 服务器的连接池。
-
缓存层面:
- 全站缓存: 使用
django.middleware.cache.UpdateCacheMiddleware 和 django.middleware.cache.FetchFromCacheMiddleware。
- 视图缓存: 使用
@cache_page 装饰器缓存整个视图的输出。
- 模板片段缓存: 使用
{% cache %} 标签缓存模板的某个部分。
- 低级缓存: 使用
cache.set() 和 cache.get() 手动缓存复杂计算结果或数据库查询结果。
-
静态文件:
- 使用 CDN (Content Delivery Network) 来分发静态文件。
- 启用 Gzip/Brotli 压缩。
-
代码层面:
- 异步任务: 将耗时操作(如发送邮件、处理图片)放入 Celery 或 RQ 等任务队列中异步执行。
- 代码优化: 避免在循环中进行数据库查询或 I/O 操作。
-
服务器层面:
- 使用高效的 WSGI 服务器(如 Gunicorn, uWSGI)和 ASGI 服务器(如 Daphne, Uvicorn)。
- 使用 Nginx 作为反向代理和静态文件服务器。
高级特性
1 什么是 Django 信号?它有什么用?
参考回答:
Django 信号是一种解耦的机制,允许某些发送者通知接收者发生了某些动作,它实现了观察者模式,允许应用中的不同部分在发生特定事件时相互通信,而无需将它们紧密耦合。
常用信号:
pre_save / post_save: 在模型保存之前/之后触发。
pre_delete / post_delete: 在模型删除之前/之后触发。
m2m_changed: 在多对多关系改变时触发。
示例: 当创建新用户时,自动创建一个关联的配置文件。
# myapp/signals.py
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.contrib.auth.models import User
from .models import UserProfile
@receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
UserProfile.objects.create(user=instance)
@receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
instance.userprofile.save()
# myapp/apps.py
from django.apps import AppConfig
class MyAppConfig(AppConfig):
name = 'myapp'
def ready(self):
import myapp.signals
项目与部署
1 Django 项目的生产环境部署流程是怎样的?
参考回答:
一个典型的生产环境部署流程如下:
-
代码准备:
- 将代码放入版本控制(如 Git)。
- 创建生产环境的分支或标签。
- 使用
requirements.txt 或 Pipfile 管理依赖。
-
环境配置:
- 设置
DEBUG = False。
- 配置
ALLOWED_HOSTS。
- 使用环境变量(如
python-decouple 或 django-environ)管理敏感信息(数据库密码、密钥等)。
-
服务器选择:
- Web 服务器: Nginx,用于处理静态文件、代理请求到应用服务器。
- 应用服务器: Gunicorn (WSGI) 或 Uvicorn (ASGI),用于运行 Django 应用。
-
部署步骤:
- 在服务器上克隆代码库。
- 创建并激活虚拟环境。
- 安装依赖 (
pip install -r requirements.txt)。
- 收集静态文件 (
python manage.py collectstatic)。
- 执行数据库迁移 (
python manage.py migrate)。
- 使用 Gunicorn 启动应用 (
gunicorn myproject.wsgi:application)。
-
进程管理:
- 使用
supervisor 或 systemd 来管理 Gunicorn 进程,确保它在崩溃后自动重启。
-
容器化 (可选):
使用 Docker 将应用和其依赖打包成镜像,使用 Docker Compose 编排 Nginx、应用和数据库服务,实现环境一致性和易于扩展。
系统设计
1 如何设计一个支持高并发的秒杀系统?
参考回答:
这是一个经典的系统设计题,考察候选人应对高并发场景的思路,关键点在于削峰和防止超卖。
核心思路:
-
前端优化:
- 按钮置灰,防止用户重复点击。
- 使用静态页面,减少服务器压力。
-
后端架构:
- 读多写少: 秒杀活动开始前,商品信息(库存)会被大量用户访问,可以将商品信息(尤其是库存)缓存到 Redis 或 Memcached 中。
- 写操作集中: 秒杀开始时,所有请求都集中在“减库存”这个操作上,这是系统的瓶颈。
防止超卖的核心策略:
-
数据库行级锁:
- 使用
SELECT ... FOR UPDATE 对库存记录加锁,确保在事务完成前,其他事务无法修改。
- 缺点: 在高并发下,锁竞争会非常激烈,性能差。
-
Redis 原子操作 (推荐):
- 使用 Redis 的
DECR 或 DECRBY 命令。DECR 是原子操作,能保证库存不会被减成负数。
- 流程:
- 用户请求到达,先从 Redis 中获取库存 (
GET stock)。
- 如果库存大于 0,执行
DECR stock。DECR 是原子操作,DECR 后的值大于等于 0,则表示抢购成功。
DECR 后的值小于 0,说明库存已空,抢购失败,需要将库存加回来 (INCR)。
- 优点: 性能极高,无锁竞争。
-
消息队列:
- 将所有秒杀请求放入一个消息队列(如 RabbitMQ, Kafka)中。
- 使用多个消费者(Worker)从队列中取请求,串行处理,将并发请求转化为串行处理。
- 优点: 削峰填谷,系统更稳定,易于扩展。
- 缺点: 引入了额外的组件,系统更复杂。
整体流程 (Redis + MQ):
- 预热: 活动开始前,将商品库存加载到 Redis。
- 用户请求: 用户点击秒杀,请求先打到 Nginx。
- 网关/限流: Nginx 或 API 网关进行简单的限流(如限制 IP QPS)。
- 应用服务: 请求到达 Django 应用。
- Redis 预检查: 应用服务先去 Redis 检查库存并尝试原子减库存,如果失败,直接返回“已售罄”。
- MQ 入队: Redis 预检查成功,将用户订单信息(如用户ID、商品ID)发送到消息队列。
- 异步下单: 消费者从队列中取出消息,执行数据库操作(创建订单、更新数据库库存),并通知用户下单成功。
开放性问题
1 你在 Django 项目中遇到的最大挑战是什么?你是如何解决的?
参考回答:
这是一个开放性问题,考察候选人的解决问题的能力和经验,回答时建议使用 STAR 法则 (Situation, Task, Action, Result)。
示例回答:
“在我上一个项目中,我们遇到了一个严重的性能瓶颈,一个报表页面的加载时间长达 30 秒。
- (Situation): 这个页面需要从多个表中聚合大量数据,并且有复杂的过滤条件。
- (Task): 我的任务是优化这个页面,将其加载时间降低到 3 秒以内。
- (Action):
- 分析: 我首先使用 Django Debug Toolbar 和数据库慢查询日志,定位到主要耗时在于一个没有索引的
JOIN 操作和一个循环查询的 N+1 问题。
- 优化: 我为相关字段添加了数据库索引,并使用
select_related 一次性获取所有关联数据,解决了 N+1 查询。
- 缓存: 我发现这个报表的数据每天只更新一次,于是我引入了
cache_page 装饰器,将报表缓存 24 小时。
- 异步: 对于必须实时计算的部分,我将其重构为一个异步任务,用户先看到“正在生成”的提示,后台任务完成后通过 WebSocket 或邮件通知用户。
- (Result): 经过这些优化,页面的加载时间成功降低到了 2 秒以内,用户体验得到了显著提升。”
参考回答: 模板继承是 Django 模板引擎的一个强大功能,它允许你创建一个基础模板,然后在子模板中重用它的结构,只覆盖需要修改的部分。
-
{% block %}: 在基础模板中定义一个或多个block,这些block是子模板可以填充的占位符。<!-- base.html --> <html> <head> <title>{% block title %}My Site{% endblock %}</title> </head> <body> {% block content %} {% endblock %} </body> </html> -
{% extends %}: 在子模板中,使用extends来指定它继承自哪个基础模板。<!-- article_detail.html --> {% extends "base.html" %} {% block title %}{{ article.title }}{% endblock %} {% block content %} <h1>{{ article.title }}</h1> <p>{{ article.body }}</p> {% endblock %}
3 Django 的 reverse 函数是做什么用的?
参考回答:
reverse 函数用于根据 URL 的名称(在 urls.py 中通过 name 参数定义)来动态生成 URL 的路径字符串,这对于代码的可维护性至关重要,因为如果 URL 路径改变,你只需要在 urls.py 中修改一处,而不需要在所有模板和视图中硬编码 URL。
示例:
-
在
urls.py中定义一个带名称的 URL:path('article/<int:pk>/', views.ArticleDetailView.as_view(), name='article_detail'), -
在模板中使用:
<a href="{% url 'article_detail' pk=article.id %}">Read more</a> -
在视图中使用:
from django.urls import reverse def some_view(request): url = reverse('article_detail', kwargs={'pk': 1}) # url 的值将是 '/article/1/'
中间件
1 什么是中间件?列举一个你使用过的中间件并说明其作用。
参考回答: 中间件是一个轻量级、低级别的“插件”系统,用于在全局范围内改变 Django 的输入或输出,它是一个 Python 类,位于请求和响应处理过程的中间,可以拦截并处理请求和响应。
常见的中间件:
django.middleware.security.SecurityMiddleware: 启用各种安全功能,如X-Content-Type-Options,X-Frame-Options,Strict-Transport-Security (HSTS)等,以及 HTTPS 重定向。django.contrib.sessions.middleware.SessionMiddleware: 处理会话,使得用户登录状态等信息可以在多个请求间保持。django.middleware.common.CommonMiddleware: 处理一些通用任务,如规范化请求字符集、处理ETag等。- 自定义中间件: 一个用于记录每个请求耗时的中间件。
自定义中间件示例 (记录请求耗时):
import time
from django.utils.deprecation import MiddlewareMixin
class RequestTimeMiddleware(MiddlewareMixin):
def process_request(self, request):
request.start_time = time.time()
def process_response(self, request, response):
total_time = time.time() - request.start_time
print(f"Request to {request.path} took {total_time:.2f} seconds.")
return response
表单
1 Django 的 ModelForm 和普通 Form 有什么区别?
参考回答:
-
Form:- 用于处理与模型无关的数据,如用户注册、联系表单等。
- 字段需要手动定义。
- 提供了数据验证和渲染 HTML 表单的功能。
-
ModelForm:- 是
Form的一个子类,与 Django 模型紧密集成。 - 可以通过
class Meta中的model和fields自动从模型生成表单字段。 - 提供了
save()方法,可以将表单数据直接保存或更新到数据库,非常方便。 - 适用于基于模型的 CRUD 操作。
- 是
示例:
# Model
from django.db import models
class Article(models.Model):= models.CharField(max_length=100)
content = models.TextField()
# ModelForm
from django.forms import ModelForm
class ArticleForm(ModelForm):
class Meta:
model = Article
fields = ['title', 'content'] # 自动生成 title 和 content 字段
# 使用
form = ArticleForm(request.POST)
if form.is_valid():
article = form.save() # 一行代码保存到数据库
认证与授权
1 如何实现一个自定义的用户模型?
参考回答:
从 Django 1.10 开始,官方推荐使用 AbstractUser 或 AbstractBaseUser 来创建自定义用户模型,而不是直接继承 User。
-
在
settings.py中指定自定义用户模型:AUTH_USER_MODEL = 'myapp.CustomUser'
-
创建自定义用户模型:
# myapp/models.py from django.contrib.auth.models import AbstractUser from django.db import models class CustomUser(AbstractUser): # 添加自定义字段 bio = models.TextField(blank=True) birth_date = models.DateField(null=True, blank=True) # 可以重写或添加方法 -
创建并应用迁移:
python manage.py makemigrations python manage.py migrate
-
创建超级用户时,需要使用自定义模型的字段:
python manage.py createsuperuser --username=myuser --email=myuser@example.com
2 解释 Django 的权限系统是如何工作的?
参考回答:
Django 的权限系统基于 auth_permission 表,它提供了细粒度的控制。
-
自动创建权限: 对于每个模型,Django 默认会创建三个权限:
add_<model_name>,change_<model_name>,delete_<model_name>。 -
权限分配:
- 用户权限: 可以直接分配给
User模型的实例。 - 组权限: 可以创建
Group,将权限分配给组,然后将用户添加到组中,用户自动继承组的所有权限。
- 用户权限: 可以直接分配给
-
在模板中使用:
{% if perms.myapp.add_article %} <a href="/create-article/">Create Article</a> {% endif %} -
在视图中使用:
from django.contrib.auth.mixins import PermissionRequiredMixin class CreateArticleView(PermissionRequiredMixin, View): permission_required = 'myapp.add_article' # ...
性能优化
1 如何优化 Django 应用的性能?
参考回答: 性能优化可以从多个层面进行:
-
数据库层面:
- 索引: 为经常用于
WHERE,JOIN,ORDER BY的字段添加数据库索引。 - 查询优化: 使用
select_related和prefetch_related避免 N+1 查询,使用only()和defer()只查询需要的字段。 - 数据库连接池: 使用
django-db-connection-pool或配置 WSGI 服务器的连接池。
- 索引: 为经常用于
-
缓存层面:
- 全站缓存: 使用
django.middleware.cache.UpdateCacheMiddleware和django.middleware.cache.FetchFromCacheMiddleware。 - 视图缓存: 使用
@cache_page装饰器缓存整个视图的输出。 - 模板片段缓存: 使用
{% cache %}标签缓存模板的某个部分。 - 低级缓存: 使用
cache.set()和cache.get()手动缓存复杂计算结果或数据库查询结果。
- 全站缓存: 使用
-
静态文件:
- 使用 CDN (Content Delivery Network) 来分发静态文件。
- 启用 Gzip/Brotli 压缩。
-
代码层面:
- 异步任务: 将耗时操作(如发送邮件、处理图片)放入 Celery 或 RQ 等任务队列中异步执行。
- 代码优化: 避免在循环中进行数据库查询或 I/O 操作。
-
服务器层面:
- 使用高效的 WSGI 服务器(如 Gunicorn, uWSGI)和 ASGI 服务器(如 Daphne, Uvicorn)。
- 使用 Nginx 作为反向代理和静态文件服务器。
高级特性
1 什么是 Django 信号?它有什么用?
参考回答: Django 信号是一种解耦的机制,允许某些发送者通知接收者发生了某些动作,它实现了观察者模式,允许应用中的不同部分在发生特定事件时相互通信,而无需将它们紧密耦合。
常用信号:
pre_save/post_save: 在模型保存之前/之后触发。pre_delete/post_delete: 在模型删除之前/之后触发。m2m_changed: 在多对多关系改变时触发。
示例: 当创建新用户时,自动创建一个关联的配置文件。
# myapp/signals.py
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.contrib.auth.models import User
from .models import UserProfile
@receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
UserProfile.objects.create(user=instance)
@receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
instance.userprofile.save()
# myapp/apps.py
from django.apps import AppConfig
class MyAppConfig(AppConfig):
name = 'myapp'
def ready(self):
import myapp.signals
项目与部署
1 Django 项目的生产环境部署流程是怎样的?
参考回答: 一个典型的生产环境部署流程如下:
-
代码准备:
- 将代码放入版本控制(如 Git)。
- 创建生产环境的分支或标签。
- 使用
requirements.txt或Pipfile管理依赖。
-
环境配置:
- 设置
DEBUG = False。 - 配置
ALLOWED_HOSTS。 - 使用环境变量(如
python-decouple或django-environ)管理敏感信息(数据库密码、密钥等)。
- 设置
-
服务器选择:
- Web 服务器: Nginx,用于处理静态文件、代理请求到应用服务器。
- 应用服务器: Gunicorn (WSGI) 或 Uvicorn (ASGI),用于运行 Django 应用。
-
部署步骤:
- 在服务器上克隆代码库。
- 创建并激活虚拟环境。
- 安装依赖 (
pip install -r requirements.txt)。 - 收集静态文件 (
python manage.py collectstatic)。 - 执行数据库迁移 (
python manage.py migrate)。 - 使用 Gunicorn 启动应用 (
gunicorn myproject.wsgi:application)。
-
进程管理:
- 使用
supervisor或systemd来管理 Gunicorn 进程,确保它在崩溃后自动重启。
- 使用
-
容器化 (可选):
使用 Docker 将应用和其依赖打包成镜像,使用 Docker Compose 编排 Nginx、应用和数据库服务,实现环境一致性和易于扩展。
系统设计
1 如何设计一个支持高并发的秒杀系统?
参考回答: 这是一个经典的系统设计题,考察候选人应对高并发场景的思路,关键点在于削峰和防止超卖。
核心思路:
-
前端优化:
- 按钮置灰,防止用户重复点击。
- 使用静态页面,减少服务器压力。
-
后端架构:
- 读多写少: 秒杀活动开始前,商品信息(库存)会被大量用户访问,可以将商品信息(尤其是库存)缓存到 Redis 或 Memcached 中。
- 写操作集中: 秒杀开始时,所有请求都集中在“减库存”这个操作上,这是系统的瓶颈。
防止超卖的核心策略:
-
数据库行级锁:
- 使用
SELECT ... FOR UPDATE对库存记录加锁,确保在事务完成前,其他事务无法修改。 - 缺点: 在高并发下,锁竞争会非常激烈,性能差。
- 使用
-
Redis 原子操作 (推荐):
- 使用 Redis 的
DECR或DECRBY命令。DECR是原子操作,能保证库存不会被减成负数。 - 流程:
- 用户请求到达,先从 Redis 中获取库存 (
GET stock)。 - 如果库存大于 0,执行
DECR stock。DECR是原子操作,DECR后的值大于等于 0,则表示抢购成功。 DECR后的值小于 0,说明库存已空,抢购失败,需要将库存加回来 (INCR)。
- 用户请求到达,先从 Redis 中获取库存 (
- 优点: 性能极高,无锁竞争。
- 使用 Redis 的
-
消息队列:
- 将所有秒杀请求放入一个消息队列(如 RabbitMQ, Kafka)中。
- 使用多个消费者(Worker)从队列中取请求,串行处理,将并发请求转化为串行处理。
- 优点: 削峰填谷,系统更稳定,易于扩展。
- 缺点: 引入了额外的组件,系统更复杂。
整体流程 (Redis + MQ):
- 预热: 活动开始前,将商品库存加载到 Redis。
- 用户请求: 用户点击秒杀,请求先打到 Nginx。
- 网关/限流: Nginx 或 API 网关进行简单的限流(如限制 IP QPS)。
- 应用服务: 请求到达 Django 应用。
- Redis 预检查: 应用服务先去 Redis 检查库存并尝试原子减库存,如果失败,直接返回“已售罄”。
- MQ 入队: Redis 预检查成功,将用户订单信息(如用户ID、商品ID)发送到消息队列。
- 异步下单: 消费者从队列中取出消息,执行数据库操作(创建订单、更新数据库库存),并通知用户下单成功。
开放性问题
1 你在 Django 项目中遇到的最大挑战是什么?你是如何解决的?
参考回答: 这是一个开放性问题,考察候选人的解决问题的能力和经验,回答时建议使用 STAR 法则 (Situation, Task, Action, Result)。
示例回答: “在我上一个项目中,我们遇到了一个严重的性能瓶颈,一个报表页面的加载时间长达 30 秒。
- (Situation): 这个页面需要从多个表中聚合大量数据,并且有复杂的过滤条件。
- (Task): 我的任务是优化这个页面,将其加载时间降低到 3 秒以内。
- (Action):
- 分析: 我首先使用 Django Debug Toolbar 和数据库慢查询日志,定位到主要耗时在于一个没有索引的
JOIN操作和一个循环查询的 N+1 问题。 - 优化: 我为相关字段添加了数据库索引,并使用
select_related一次性获取所有关联数据,解决了 N+1 查询。 - 缓存: 我发现这个报表的数据每天只更新一次,于是我引入了
cache_page装饰器,将报表缓存 24 小时。 - 异步: 对于必须实时计算的部分,我将其重构为一个异步任务,用户先看到“正在生成”的提示,后台任务完成后通过 WebSocket 或邮件通知用户。
- 分析: 我首先使用 Django Debug Toolbar 和数据库慢查询日志,定位到主要耗时在于一个没有索引的
- (Result): 经过这些优化,页面的加载时间成功降低到了 2 秒以内,用户体验得到了显著提升。”
