首次提交

This commit is contained in:
2026-09-23 13:56:49 +08:00
parent 7c235aab6e
commit 0e47b1bba4
12 changed files with 364 additions and 176 deletions
+23
View File
@@ -0,0 +1,23 @@
# ============================================================
# 使用说明:复制本文件为 .env,然后填入真实的配置值
# 命令: cp .env.example .env
# ⚠️ .env 文件已被 .gitignore 忽略,不会被提交到 Git
# ============================================================
# Flask 会话签名密钥(生产环境必须替换为随机字符串)
# 生成方式: python -c "import secrets; print(secrets.token_hex(32))"
SECRET_KEY=change-me-to-random-string
# 数据库连接 URI
# 格式: dialect+driver://username:password@host:port/database?params
# 默认使用 SQLite 方便本地开发,零配置即可运行
DATABASE_URL=sqlite:///app.db
# MySQL 示例(需安装 PyMySQL):
# DATABASE_URL=mysql+pymysql://root:your_password@localhost:3306/mydb?charset=utf8mb4
# PostgreSQL 示例(需安装 psycopg2-binary):
# DATABASE_URL=postgresql+psycopg2://user:your_password@localhost:5432/mydb
# 数据库连接池大小(仅生产环境生效,根据服务器内存调整)
DB_POOL_SIZE=10
+35 -161
View File
@@ -1,176 +1,50 @@
# ---> Python
# Byte-compiled / optimized / DLL files
# ==================== Python 运行时产物 ====================
# 字节码缓存目录,每次运行自动生成,无需版本控制
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# 虚拟环境目录(每个开发者本地创建,不纳入仓库)
venv/
env/
.venv/
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
# ==================== Flask 框架产物 ====================
# Flask 默认实例文件夹(存放 SQLite、日志等运行时文件)
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# UV
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
#uv.lock
# poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock
# pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
# in version control.
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control
.pdm.toml
.pdm-python
.pdm-build/
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
# ==================== ⚠️ 安全相关(最重要) ====================
# 环境变量文件:包含数据库密码、密钥等敏感信息,绝对禁止提交!
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
.env.*
# 但保留示例文件,让新成员知道需要配置哪些变量
!.env.example
# Spyder project settings
.spyderproject
.spyproject
# ==================== IDE 与操作系统 ====================
.vscode/
.idea/
.DS_Store
Thumbs.db
# Rope project settings
.ropeproject
# ==================== 数据库文件 ====================
# SQLite 数据库文件是运行时生成的,不应纳入版本控制
*.db
*.sqlite3
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
# PyCharm
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
# Ruff stuff:
.ruff_cache/
# PyPI configuration file
.pypirc
# ==================== 测试与覆盖率 ====================
htmlcov/
.coverage
.coverage.*
.pytest_cache/
.tox/
.nox/
.cache/
# ==================== 构建/分发产物 ====================
build/
dist/
*.egg-info/
*.egg
+71 -15
View File
@@ -1,19 +1,75 @@
# flask_example
flask框架和连接数据库模板
> 基于 Flask 3.x + SQLAlchemy 2.0 的生产级项目模板
> 内置应用工厂、数据库迁移、安全配置和 CRUD 示例
## 🚀 快速开始
```bash
# 1. 克隆项目
git clone <your-repo-url> && cd flask-db-template
# 2. 创建虚拟环境并安装依赖
python -m venv venv
source venv/bin/activate # macOS/Linux
# venv\Scripts\activate # Windows
pip install -r requirements.txt
# 3. 配置环境变量(⚠️ 必须执行)
cp .env.example .env
# 编辑 .env,至少修改 SECRET_KEY 和 DATABASE_URL
# 4. 初始化数据库
flask db init # 首次执行,生成 migrations/ 目录
flask db migrate -m "init" # 生成迁移脚本
flask db upgrade # 应用迁移到数据库
# 5. 启动开发服务器
python run.py
```
my_flask_app/
## 📡 API 接口
| 方法 | 路径 | 说明 | 请求体 |
|------|------|------|--------|
| `POST` | `/api/users` | 创建用户 | `{"username":"test","email":"t@example.com"}` |
| `GET` | `/api/users?page=1&per_page=20` | 分页查询用户 | - |
## ⚠️ 安全须知
- **永远不要**将 `.env` 提交到 Git(已在 `.gitignore` 中排除)
- 生产部署使用 Gunicorn:`gunicorn -w 4 -b 0.0.0.0:8000 "run:app"`
- 修改模型后**必须**执行 `flask db migrate` + `flask db upgrade`
- `debug=True` 仅限开发环境,生产环境开启会导致源码泄露
## 📂 项目结构说明
```
flask-db-template/
├── app/
│ ├── __init__.py # 应用工厂 & 扩展初始化
│ ├── config.py # 配置文件
│ ├── models/ # 数据模型
│ │ ├── __init__.py
│ │ └── user.py
│ └── routes/ # 蓝图/路由
│ ├── __init__.py
│ └── user.py
├── migrations/ # 数据库迁移文件 (Flask-Migrate)
├── .env # 环境变量(不要提交到Git)
├── requirements.txt
└── run.py # 启动入口
```
│ ├── __init__.py # 应用工厂:创建Flask实例并初始化扩展
│ ├── config.py # 配置管理:集中管理所有环境变量和数据库配置
│ ├── models/
│ │ ├── __init__.py # 模型统一导出:确保迁移工具能发现所有表
│ │ └── user.py # 用户模型:定义数据表结构和序列化方法
│ └── routes/
│ ├── __init__.py # 蓝图统一导出
│ └── user.py # 用户API:包含完整的CRUD和异常处理
├── migrations/ # 数据库迁移目录(由flask db init生成)
│ └── .gitkeep # Git空目录占位符
├── .env.example # 环境变量模板(不含真实密码,可安全提交)
├── .gitignore # Git忽略规则(防止敏感文件和缓存被提交)
├── README.md # 项目文档
├── requirements.txt # Python依赖清单(锁定版本保证环境一致)
└── run.py # 启动入口:开发环境启动脚本
```
| 文件/目录 | 作用 |
|-----------|------|
| `app/__init__.py` | 应用工厂,创建 Flask 实例并初始化扩展 |
| `app/config.py` | 集中配置管理,从环境变量读取敏感信息 |
| `app/models/` | SQLAlchemy 数据模型定义 |
| `app/routes/` | Blueprint 路由模块,按资源拆分 |
| `migrations/` | Alembic 数据库迁移脚本 |
| `.env.example` | 环境变量模板(安全提交到 Git) |
| `run.py` | 开发环境启动入口 |
+35
View File
@@ -0,0 +1,35 @@
"""
应用工厂模块(Application Factory Pattern)
- 延迟创建 Flask 实例,支持测试时传入不同配置
- 扩展对象在模块顶层创建但不绑定 app,通过 init_app() 延迟绑定
- 这种模式是 Flask 官方推荐的最佳实践,避免循环导入
"""
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from app.config import Config
# ✅ 扩展实例在工厂外部创建,不与任何 app 绑定
# 这样可以在测试中创建多个 app 实例共享同一套扩展
db = SQLAlchemy()
migrate = Migrate()
def create_app(config_class=Config):
"""
应用工厂函数
:param config_class: 配置类,默认使用 Config,测试时可替换为 TestingConfig
:return: 配置完成的 Flask 应用实例
"""
app = Flask(__name__)
app.config.from_object(config_class)
# 将扩展绑定到当前 app 实例
db.init_app(app)
migrate.init_app(app, db)
# 注册蓝图(放在这里避免循环导入:蓝图引用 db → db 在工厂中初始化)
from app.routes.user import user_bp
app.register_blueprint(user_bp, url_prefix='/api/users')
return app
+37
View File
@@ -0,0 +1,37 @@
"""
应用配置模块
- 集中管理所有配置项,支持多环境切换
- 通过 python-dotenv 自动加载 .env 文件中的环境变量
- 敏感信息全部从环境变量读取,绝不硬编码
"""
import os
from dotenv import load_dotenv
# 自动加载项目根目录下的 .env 文件
load_dotenv()
class Config:
"""基础配置类(开发/生产通用)"""
# Flask 会话加密密钥,用于 cookie 签名和 CSRF 保护
SECRET_KEY = os.environ.get('SECRET_KEY', 'dev-secret-key-change-in-prod')
# 数据库连接字符串,优先读取环境变量,回退到本地 SQLite
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL', 'sqlite:///app.db')
# 关闭 SQLAlchemy 事件追踪,避免不必要的内存开销和警告
SQLALCHEMY_TRACK_MODIFICATIONS = False
# 是否打印 SQL 语句(开发调试时可设为 True,生产环境务必关闭)
SQLALCHEMY_ECHO = False
# 数据库引擎高级选项(主要针对 MySQL/PostgreSQL 连接池)
SQLALCHEMY_ENGINE_OPTIONS = {
# 连接池常驻连接数
'pool_size': int(os.environ.get('DB_POOL_SIZE', 10)),
# 连接最大存活时间(秒),防止数据库端主动断开空闲连接
'pool_recycle': 3600,
# 每次从池中取出连接前先 ping 一下,避免拿到已断开的死连接
'pool_pre_ping': True,
}
+9
View File
@@ -0,0 +1,9 @@
"""
模型统一导出模块
- Alembic(Flask-Migrate 底层)通过扫描此文件发现所有模型
- 新增模型后必须在此处添加 import,否则 `flask db migrate` 无法检测到新表
- __all__ 明确声明公开接口,避免意外导入内部模块
"""
from app.models.user import User
__all__ = ['User']
+42
View File
@@ -0,0 +1,42 @@
"""
用户数据模型
- 定义 users 表结构、字段约束和索引
- 提供 to_dict() 方法用于 API 响应序列化
- 注意:不要在模型中直接 import Blueprint 或路由,避免循环导入
"""
from datetime import datetime
from app import db
class User(db.Model):
"""用户表模型"""
__tablename__ = 'users'
# 主键:自增整数,InnoDB 聚簇索引默认按主键排序
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
# 用户名:唯一约束 + B-tree 索引,加速查询和去重校验
username = db.Column(db.String(80), unique=True, nullable=False, index=True)
# 邮箱:唯一约束,业务层应额外做格式校验
email = db.Column(db.String(120), unique=True, nullable=False)
# 创建时间:默认值为记录插入时的 UTC 时间
created_at = db.Column(db.DateTime, default=datetime.utcnow)
def to_dict(self):
"""
将模型实例序列化为字典
- 用于 API JSON 响应,避免直接暴露内部字段(如密码哈希)
- datetime 转为 ISO 8601 字符串,前端可直接解析
"""
return {
'id': self.id,
'username': self.username,
'email': self.email,
'created_at': self.created_at.isoformat(),
}
def __repr__(self):
"""调试友好的字符串表示,在 Flask shell 和日志中显示"""
return f'<User {self.username}>'
+8
View File
@@ -0,0 +1,8 @@
"""
蓝图统一导出模块
- 当蓝图数量增多时,在此集中管理,简化 __init__.py 中的注册逻辑
- 新增蓝图后只需在此添加一行 import
"""
from app.routes.user import user_bp
__all__ = ['user_bp']
+64
View File
@@ -0,0 +1,64 @@
"""
用户 API 路由模块
- 使用 Blueprint 实现模块化路由,避免单文件膨胀
- 包含参数校验、异常处理和分页查询的生产级实现
- 所有数据库写操作均包裹在 try/except 中,失败时自动回滚
"""
from flask import Blueprint, request, jsonify
from app import db
from app.models.user import User
# 创建蓝图,名称用于 url_for() 反向解析,__name__ 用于定位模板/静态文件
user_bp = Blueprint('user', __name__)
@user_bp.route('', methods=['POST'])
def create_user():
"""
创建新用户
- POST /api/users
- Body: {"username": "string", "email": "string"}
- Returns: 201 + 用户JSON / 400 + 错误信息 / 500 + 服务器错误
"""
# silent=True 使得非 JSON 请求体返回 None 而非抛出 400 异常
data = request.get_json(silent=True)
# ✅ 入参校验:永远不要信任客户端输入
if not data or 'username' not in data or 'email' not in data:
return jsonify({'error': 'Missing required fields: username, email'}), 400
try:
user = User(username=data['username'], email=data['email'])
db.session.add(user)
# commit 同时完成 flush(发送SQL)和事务提交
db.session.commit()
return jsonify(user.to_dict()), 201
except Exception as e:
# ✅ 异常时必须回滚,否则后续请求复用该 session 会持续报错
db.session.rollback()
# 生产环境应记录日志而非直接返回异常详情,此处为模板演示
return jsonify({'error': str(e)}), 500
@user_bp.route('', methods=['GET'])
def get_users():
"""
分页获取用户列表
- GET /api/users?page=1&per_page=20
- Returns: 分页结果 JSON,包含 items、total、pages、current_page
"""
# type=int 确保参数被转换为整数,非法值回退到默认值
page = request.args.get('page', 1, type=int)
per_page = request.args.get('per_page', 20, type=int)
# paginate() 自动处理 LIMIT/OFFSET,error_out=False 避免页码越界时抛异常
pagination = User.query.order_by(User.created_at.desc()).paginate(
page=page, per_page=per_page, error_out=False
)
return jsonify({
'items': [u.to_dict() for u in pagination.items],
'total': pagination.total, # 总记录数
'pages': pagination.pages, # 总页数
'current_page': page, # 当前页码
})
+3
View File
@@ -0,0 +1,3 @@
# 此文件唯一作用:让 Git 跟踪 migrations/ 空目录
# Git 不支持空目录,没有此文件则 clone 后缺少 migrations/ 目录
# 执行 `flask db init` 后此文件会被实际迁移文件替代,可删除
+17
View File
@@ -0,0 +1,17 @@
# Flask 核心框架(3.x 支持异步和现代特性)
Flask==3.0.0
# ORM 扩展:将数据库操作映射为 Python 对象
Flask-SQLAlchemy==3.1.1
# 数据库迁移工具:基于 Alembic,自动追踪模型变更并生成迁移脚本
Flask-Migrate==4.0.5
# 环境变量加载:从 .env 文件读取配置,避免硬编码敏感信息
python-dotenv==1.0.0
# MySQL 驱动:纯 Python 实现,兼容性好(PostgreSQL 请替换为 psycopg2-binary)
PyMySQL==1.1.0
# PyMySQL 加密依赖:支持 MySQL 8.0+ 的 caching_sha2_password 认证
cryptography==41.0.7
+20
View File
@@ -0,0 +1,20 @@
"""
开发环境启动入口
- 仅在本地开发时使用,生产环境请用 Gunicorn/uWSGI
- 启动前自动建表(仅限开发便利,生产环境必须使用 flask db upgrade)
- debug=True 开启热重载和交互式调试器(⚠️ 生产环境严禁开启)
"""
from app import create_app, db
app = create_app()
if __name__ == '__main__':
# 在应用上下文中执行数据库操作
with app.app_context():
# ⚠️ create_all() 仅用于开发快速验证
# 生产环境请使用: flask db migrate && flask db upgrade
db.create_all()
# host='0.0.0.0' 允许外部访问(Docker/局域网调试需要)
# 仅监听 127.0.0.1 则只允许本机访问
app.run(debug=True, host='0.0.0.0', port=5000)