3. 项目骨架搭建:Django项目创建、目录结构规范、配置文件分层设计

好,咱们进入第三章。说实话,很多新手拿到一个项目,上来就 django-admin startproject 一把梭。结果呢?项目跑起来了,但三个月后,连自己都看不懂自己写的配置。我见过太多这样的烂摊子了。

这一章,我就带你搭一个「能打硬仗」的项目骨架。不是花架子,是真正能在生产环境里扛住压力的结构。

3.1 创建Django项目,别急着敲命令

先想清楚一件事:你的项目是给谁用的?是给内部运营的小工具,还是面向百万用户的电商平台?

我个人习惯,在创建项目之前,先画一个简单的目录树。哪怕只是在纸上画两笔。这能帮你理清思路。

好,假设我们要做一个电商后台。先创建项目:

# 创建虚拟环境
python -m venv venv
source venv/bin/activate  # Windows用 venv\Scripts\activate

# 安装Django
pip install django

# 创建项目
django-admin startproject ecommerce_backend
cd ecommerce_backend

嗯,这里要注意。默认生成的目录结构太「平」了。所有东西都堆在根目录下,看着就头疼。我们需要动动手,把它改造成更适合团队协作的样子。

3.2 目录结构规范:让每个人都能找到东西

我在项目中遇到过最痛苦的事,就是找一个配置文件要翻五个文件夹。后来我总结了一套结构,用了好几年,团队里新人也很快能上手。

改造后的目录结构长这样:

ecommerce_backend/
├── apps/                    # 所有业务应用放这里
│   ├── users/               # 用户模块
│   ├── products/            # 商品模块
│   ├── orders/              # 订单模块
│   └── payments/            # 支付模块
├── config/                  # 配置文件集中管理
│   ├── settings/            # 分层配置
│   │   ├── base.py          # 公共配置
│   │   ├── dev.py           # 开发环境
│   │   ├── prod.py          # 生产环境
│   │   └── test.py          # 测试环境
│   ├── urls.py              # 根路由
│   └── wsgi.py              # WSGI入口
├── libs/                    # 公共库、工具函数
│   ├── utils/               # 通用工具
│   └── middleware/          # 自定义中间件
├── static/                  # 静态文件
├── media/                   # 用户上传文件
├── templates/               # 模板文件
├── scripts/                 # 运维脚本、管理命令
├── docs/                    # 项目文档
├── requirements/            # 依赖管理
│   ├── base.txt
│   ├── dev.txt
│   └── prod.txt
├── manage.py
└── .env                     # 环境变量(不提交到git)

核心原则:按功能分层,而不是按文件类型分层。把「配置」放一起,把「业务」放一起,把「工具」放一起。这样你找东西的时候,脑子不用切换上下文。

你想想看,如果所有配置都散落在各个app里,改个数据库连接要翻遍整个项目。集中管理之后,改配置只需要去 config/settings/ 这一个地方。

3.3 配置文件分层设计:为什么需要三层?

说白了,开发环境和生产环境的需求完全不同。开发时你需要调试工具、宽松的跨域设置;生产环境则需要关闭调试、开启缓存、严格的安全策略。

我曾经见过一个项目,开发和生产用同一个配置文件。每次上线前,手动改 DEBUG = True 为 False。结果有一次忘了改,线上直接暴露了源码路径……嗯,从那以后,我再也不敢用单文件配置了。

分层设计的好处:

配置层 用途 典型内容
base.py 所有环境共享 INSTALLED_APPS、数据库引擎、时区、语言
dev.py 本地开发 DEBUG=True、SQLite、django-debug-toolbar
prod.py 生产环境 DEBUG=False、PostgreSQL、Redis缓存、HTTPS
test.py 自动化测试 内存数据库、快速密码哈希器

具体怎么实现?看代码:

# config/settings/base.py
import os
from pathlib import Path

BASE_DIR = Path(__file__).resolve().parent.parent.parent

SECRET_KEY = os.environ.get('DJANGO_SECRET_KEY', 'fallback-dev-key')

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    # ... 其他公共app
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    # ... 公共中间件
]

ROOT_URLCONF = 'config.urls'

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [BASE_DIR / 'templates'],
        'APP_DIRS': True,
        # ...
    },
]

# 数据库配置只留引擎,具体参数由子类覆盖
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
    }
}

# 时区、语言等公共设置
LANGUAGE_CODE = 'zh-hans'
TIME_ZONE = 'Asia/Shanghai'
USE_I18N = True
USE_TZ = True
# config/settings/dev.py
from .base import *

DEBUG = True

ALLOWED_HOSTS = ['*']

INSTALLED_APPS += [
    'debug_toolbar',
]

MIDDLEWARE += [
    'debug_toolbar.middleware.DebugToolbarMiddleware',
]

# 开发环境用本地文件存储
STATIC_URL = '/static/'
STATICFILES_DIRS = [BASE_DIR / 'static']

MEDIA_URL = '/media/'
MEDIA_ROOT = BASE_DIR / 'media'

# 打印SQL日志
LOGGING = {
    'version': 1,
    'handlers': {
        'console': {
            'level': 'DEBUG',
            'class': 'logging.StreamHandler',
        },
    },
    'loggers': {
        'django.db.backends': {
            'handlers': ['console'],
            'level': 'DEBUG',
        },
    },
}
# config/settings/prod.py
from .base import *
import os

DEBUG = False

ALLOWED_HOSTS = ['yourdomain.com', 'www.yourdomain.com']

# 生产环境用PostgreSQL
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': os.environ['DB_NAME'],
        'USER': os.environ['DB_USER'],
        'PASSWORD': os.environ['DB_PASSWORD'],
        'HOST': os.environ['DB_HOST'],
        'PORT': os.environ.get('DB_PORT', '5432'),
    }
}

# 静态文件由Nginx处理
STATIC_ROOT = '/var/www/static'
STATIC_URL = '/static/'

MEDIA_ROOT = '/var/www/media'
MEDIA_URL = '/media/'

# 安全配置
SECURE_SSL_REDIRECT = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True

# 缓存用Redis
CACHES = {
    'default': {
        'BACKEND': 'django_redis.cache.RedisCache',
        'LOCATION': os.environ['REDIS_URL'],
        'OPTIONS': {
            'CLIENT_CLASS': 'django_redis.client.DefaultClient',
        }
    }
}

小技巧:在 manage.py 中通过环境变量 DJANGO_SETTINGS_MODULE 来指定使用哪个配置。比如本地开发时:export DJANGO_SETTINGS_MODULE=config.settings.dev。这样你就不用改任何代码,切换环境只需要改一个环境变量。

3.4 如何选择配置?用环境变量驱动

你可能会问:每次手动改环境变量不也麻烦吗?

没错,所以我们需要一个 .env 文件。配合 python-decouple 或 django-environ 这类库,自动加载环境变量。

# .env 文件(不提交到git)
DJANGO_SETTINGS_MODULE=config.settings.dev
DJANGO_SECRET_KEY=your-secret-key-here
DB_NAME=ecommerce_db
DB_USER=postgres
DB_PASSWORD=strong_password_123
# manage.py 顶部加入
import os
import dotenv

dotenv.load_dotenv()

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings.dev')

这样一来,每个开发者只需要维护自己的 .env 文件。生产环境由运维同学在部署时注入真实的环境变量。代码里不出现任何敏感信息。

警告:千万不要把 .env 文件提交到Git仓库!在 .gitignore 中加上 .env。我见过有人把数据库密码直接写死在代码里,然后上传到GitHub……那场面,简直是灾难。

3.5 启动验证:检查你的骨架是否稳固

结构搭好了,我们来验证一下:

# 设置环境变量
export DJANGO_SETTINGS_MODULE=config.settings.dev

# 运行迁移
python manage.py migrate

# 启动开发服务器
python manage.py runserver

# 如果能正常启动,说明骨架没问题
# 访问 http://127.0.0.1:8000 看到Django欢迎页

如果报错,多半是路径问题。检查 BASE_DIR 的计算是否正确。我刚开始用这种分层结构时,也经常搞错路径层级。后来我习惯在 base.py 里加一行 print(BASE_DIR),确认路径对了再继续。

3.6 小结:骨架决定了项目能长多大

这一章我们做了三件事:

  1. 创建了项目,但没直接用默认结构
  2. 重构了目录,让业务、配置、工具各归其位
  3. 分层了配置,用环境变量驱动不同环境

这套骨架我用了好几年,从几十万用户的小项目到千万级流量的平台,都扛得住。说白了,好的骨架不是一蹴而就的,而是在一次次重构中打磨出来的。但至少,我们从第一天开始就别给自己挖坑。

下一章,我们会在这个骨架上填充血肉——开始搭建用户模块。到时候你会看到,今天搭的骨架有多重要。