Django项目结构如何规划?从零搭建可维护、可扩展的企业级架构
目录导读
- 为什么要重视Django项目结构规划?
- 基础项目结构:从
django-admin startproject开始 - 企业级项目结构推荐方案(含目录详解)
- 关键模块拆分原则:App与功能如何划分?
- 配置文件管理:环境分离的三种方式
- URL路由体系:从扁平到模块化
- 静态文件与媒体文件的最佳实践
- 测试与文档的目录组织
- 常见问题与避坑指南(Q&A)
- 一套可复用的模板
为什么要重视Django项目结构规划?
很多初学者用django-admin startproject myproject创建项目后,就把所有代码堆在views.py和models.py里,当项目扩展到10个App、20个模型、50个视图时,这种“平铺式”结构会带来三大灾难:
- 循环导入:模块间相互引用,Python解释器崩溃
- 配置混乱:本地、测试、生产环境的配置混杂在一起
- 团队协作低效:每个人按自己的习惯创建文件,代码审查变成“考古”
SEO优化提示:Google和Bing的爬虫更喜欢结构清晰、加载快速的网站,一个良好的项目结构能让Django更高效地处理请求(比如合理使用缓存和静态文件),间接提升SEO排名。
基础项目结构:从django-admin startproject开始
Django默认生成的结构如下:
myproject/
manage.py
myproject/
__init__.py
settings.py
urls.py
wsgi.py
asgi.py
这个结构只适合“玩具项目”,真实项目中,我们需要:
- 将
settings.py拆分为多环境配置文件 - 创建独立的
apps/目录统一管理App - 添加
config/目录存放第三方配置 - 增加
docs/和tests/顶层目录
企业级项目结构推荐方案(含目录详解)
经过上百个生产项目的验证,我推荐以下结构(以电商项目eshop为例):
eshop/
manage.py # 项目管理入口
requirements/ # 依赖管理
base.txt # 通用依赖
local.txt # 本地开发依赖(含调试工具)
production.txt # 生产环境依赖(不含调试)
config/ # 配置集
settings/
__init__.py
base.py # 基础配置(所有环境共享)
local.py # 本地开发配置
production.py # 生产配置(密钥不提交Git)
test.py # 测试配置(使用内存数据库)
urls/
__init__.py # 主URL路由
app_urls.py # App路由汇总(可选)
wsgi.py
asgi.py
apps/ # 所有App集中管理
accounts/ # 用户账户App
products/ # 商品App
orders/ # 订单App
payments/ # 支付App
common/ # 通用工具App(自定义权限、中间件等)
static/ # 静态文件(CSS/JS/图片)
media/ # 用户上传文件
templates/ # 项目级模板
base.html # 基础模板
includes/ # 可复用组件
accounts/
products/
utils/ # 全局工具函数
__init__.py
validators.py # 自定义验证器
helpers.py # 通用辅助函数
exceptions.py # 自定义异常类
tests/ # 集成测试与端到端测试
conftest.py # pytest配置
test_orders.py
docs/ # 项目文档
api.md
deployment.md
scripts/ # 运维脚本
seed_data.py # 填充测试数据
backup_db.sh
docker/ # Docker相关
Dockerfile
docker-compose.yml
.env.example # 环境变量模板(不提交真实密钥)
.gitignore
README.md
pyproject.toml # 现代Python项目元数据
关键设计原则:
- 配置与代码分离:
settings/目录支持多环境,敏感信息用环境变量 - App集中管理:所有业务模块放在
apps/下,避免散落在根目录 - 分层测试:单元测试放在App内部,集成测试放在顶层
tests/
关键模块拆分原则:App与功能如何划分?
很多开发者纠结“一个功能该放哪个App”,遵循以下两条黄金法则:
单一职责原则
一个App只做一件事,比如accounts只负责用户注册/登录/密码重置,不负责用户资料展示(那是profilesApp的事)。
数据模型驱动划分
如果两个模型之间存在外键关系且数据交互频繁,它们应放在同一个App里,例如Order和OrderItem天然属于ordersApp。
实战问答:
问:用户头像上传业务该放
accounts还是新建profiles?
答:如果头像只是用户的扩展信息,放accounts即可,如果包含复杂的裁剪、水印、审核流程,建议拆分为独立的avatarsApp,关键在于“业务复杂度”。
配置文件管理:环境分离的三种方式
多文件继承(推荐)
在config/settings/中创建base.py(通用配置)、local.py(本地配置)、production.py(生产配置)。
# base.py
INSTALLED_APPS = ['django.contrib.admin', ...]
DATABASES = {'default': {'ENGINE': 'django.db.backends.postgresql'}}
# local.py
from .base import *
DEBUG = True
DATABASES['default']['NAME'] = 'eshop_dev'
# production.py
from .base import *
DEBUG = False
DATABASES['default']['NAME'] = os.environ.get('DB_NAME')
通过DJANGO_SETTINGS_MODULE=config.settings.local启动。
环境变量驱动(适合Docker)
用django-environ库读取.env文件,所有差异都通过变量控制。
单一文件+条件判断
不推荐!一个settings.py写满if DEBUG:,容易导致测试环境误用生产密钥。
URL路由体系:从扁平到模块化
反例:把所有路由写在config/urls.py里,长达300行。
正例:每个App内部有自己的urls.py,主路由只负责include。
# config/urls.py
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('api/v1/accounts/', include('apps.accounts.urls')),
path('api/v1/products/', include('apps.products.urls')),
]
# apps/accounts/urls.py
from rest_framework.routers import DefaultRouter
from .views import UserViewSet
router = DefaultRouter()
router.register('users', UserViewSet)
urlpatterns = router.urls
SEO优化提示:清晰的URL结构(如/api/v1/products/categories/)比无序的/api/?type=prod&cat=5更受搜索引擎青睐。
静态文件与媒体文件的最佳实践
- 开发环境:Django自带
django.contrib.staticfiles,用python manage.py runserver自动处理。 - 生产环境:使用Nginx/Apache直接服务
static/和media/,避免Django处理静态文件(性能瓶颈)。 - 云存储:当媒体文件超过10GB时,使用AWS S3/阿里云OSS,Django通过
django-storages库透明对接。
目录约定:
static/ # 项目级静态文件(CSS/JS/字体)
media/ # 用户动态上传(头像、商品图片)
# App内部静态文件放 apps/products/static/products/ 下(命名空间)
注意:.gitignore中应排除media/,但保留static/(模板中的静态文件需版本控制)。
测试与文档的目录组织
测试双轨制:
- 单元测试:在App内部创建
tests.py(或tests/目录),测试模型方法、表单验证等。 - 集成测试:放在顶层
tests/,测试多App交互、API接口、数据库事务。
文档三件套:
README.md:项目简介、快速启动步骤、环境要求docs/api.md:接口文档(可自动生成于DRF的swagger)docs/deployment.md:部署步骤、环境变量列表
Q&A:
问:测试文件放在App内部好还是统一目录好?
答:两者都要,App内部放单元测试(与代码高度耦合),顶层放集成测试(模拟用户真实操作流)。
常见问题与避坑指南(Q&A)
Q1:App太多,如何防止循环导入?
A:在apps/__init__.py中定义App的加载顺序,如果App A需要导入App B的模型,使用django.apps的惰性加载:from django.apps import apps; MyModel = apps.get_model('products', 'Product')。
Q2:配置文件中的密钥如何管理?
A:永远不要提交.env文件到Git!创建.env.example作为模板,在生产服务器用环境变量或Vault/Secret Manager注入真实密钥。
Q3:静态文件在反向代理后路径错误?
A:检查STATIC_URL是否与Nginx的location匹配。
location /static/ {
alias /path/to/eshop/static/;
}
确保Django的STATIC_ROOT与Nginx的alias路径一致。
Q4:Django项目结构是否适用微服务?
A:建议按业务边界拆分为独立的Django项目(如用户服务、商品服务),项目内部再使用本文结构,仍可保持apps/和config/的布局。
一套可复用的模板
本文提供的项目结构经过多个生产项目验证,能同时满足:
- 可维护性:5人以上团队也能清晰分工
- 可扩展性:添加新App只需在
apps/下创建目录并include路由 - SEO友好:结构化URL、清晰静态文件处理、高性能配置
快速上手:可以使用django-cookiecutter模板(如cookiecutter-django)一键生成类似结构,再根据业务定制。
最终建议:结构不是目的,而是手段,如果项目只有1个App加3个页面,无需过度设计,但一旦预见到项目会增长,请立即按本文方法重构——否则成本将随代码量指数级上升。