environment-setup: Python virtual environment management,pip install flaskwith the first canonical App

📂 Stage: Stage 1 - Breaking the ice and setting sail (Basics) 🔗 Related chapters: 初识 Flask · 路由(Routing)艺术


1. Why is it necessary to use a virtual environment?

1.1 Fatal pitfalls of global installation

Imagine a scenario:

场景:你的老项目A依赖Flask 2.0,新项目B要尝鲜Flask 3.0的异步装饰器
→ 老项目全局更新Flask?直接报兼容性错误
→ 新项目迁就全局版本?错过新特性
→ 😱 只能拆机器?当然不是!

Virtual environment is the antidote: It creates an independent "Python environment box" for each project, and the dependencies and interpreter paths inside are completely isolated and do not interfere with each other.

1.2 Python 3.3+ comes withvenvThat's enough

May have been used beforevirtualenvPipenv, but now Python officials have put lightweight and easy-to-usevenvBuilt-in (3.3+), no need to install any additional third-party tools.

Commonly used creation & activation commands

# 1. 创建虚拟环境(推荐用当前目录下的 venv 文件夹作为容器)
python -m venv venv

# 2. 激活虚拟环境(根据终端选择)
# ✅ Windows PowerShell(推荐先执行 Set-ExecutionPolicy RemoteSigned -Scope CurrentUser 解决权限限制)
.\venv\Scripts\Activate.ps1
# ✅ Windows CMD
venv\Scripts\activate.bat
# ✅ macOS / Linux(bash/zsh通用)
source venv/bin/activate

# 3. 验证激活成功(命令提示符前会带 (venv) 前缀)
(venv) PS D:\code_daoman\daoman_blog>

2. Install Flask and standardize dependency management

2.1 Basic start: only install Flask

If you are writing an exercise project, just install the core package directly:

pip install flask

2.2 Practical recommendation: Install commonly used extensions

When doing formal projects (such as this series of DaoMan blogs), we will use these high-frequency extensions:

pip install \
flask \                 # 核心 Web 框架
flask-sqlalchemy \      # ORM 数据库操作
flask-login \           # 用户认证
flask-wtf \             # 表单验证+CSRF防护
flask-migrate \         # Alembic 数据库迁移封装
flask-cors \            # 跨域请求处理
python-dotenv \         # 环境变量加载
email-validator         # 邮箱格式验证

2.3 Dependency list: necessary for team/deployment

After each installation/uninstallation of dependencies, be sure to update the dependency list to facilitate one-click recovery on the new machine:

# 导出当前环境的依赖到 requirements.txt
pip freeze > requirements.txt

# 新机器/新克隆项目后,一键恢复所有依赖
pip install -r requirements.txt

Full exportpip freezeIt may bring some unnecessary indirect dependencies. It is recommended to manually write core dependencies + version range, which is clear and safe (to avoid small version bugs of indirect dependencies):

Flask>=3.0.0,<4.0.0
Flask-SQLAlchemy>=3.1.0,<4.0.0
Flask-Login>=0.6.3,<1.0.0
Flask-WTF>=1.2.0,<2.0.0
Flask-Migrate>=4.0.0,<5.0.0
Flask-CORS>=4.0.0,<5.0.0
python-dotenv>=1.0.0,<2.0.0
email-validator>=2.0.0,<3.0.0

3. Project initialization (DaoMan blog as an example)

3.1 Quickly build the project skeleton

Follow the following command step by step to generate a standard Flask project directory:

# 1. 创建项目根目录并进入
mkdir daoman_blog
cd daoman_blog

# 2. 初始化虚拟环境(记得激活!)
python -m venv venv
# 根据终端选择激活命令:.\venv\Scripts\Activate.ps1 或 source venv/bin/activate

# 3. 创建目录结构(手动 mkdir 或按下面的树形结构创建)
daoman_blog/
├── app/                       # 应用核心代码包(重要!单独放代码)
│   ├── __init__.py           # 应用工厂入口(create_app()函数)
│   ├── extensions.py         # 扩展统一初始化(db/login_manager等)
│   ├── routes/               # 路由模块(用 Blueprint 拆分)
│   │   ├── __init__.py
│   │   ├── main.py           # 首页/通用路由
│   │   ├── auth.py           # 登录/注册/登出路由
│   │   └── articles.py       # 文章相关路由
│   ├── models/               # ORM 数据模型
│   │   ├── __init__.py
│   │   └── user.py
│   ├── templates/            # Jinja2 模板文件
│   │   ├── base.html         # 基础母模板(所有页面继承)
│   │   ├── index.html        # 首页
│   │   └── auth/
│   │       └── login.html    # 登录页
│   └── static/               # 静态资源(CSS/JS/图片/上传文件)
│       ├── css/
│       ├── js/
│       └── uploads/
├── migrations/               # Alembic 数据库迁移脚本(自动生成)
├── tests/                    # 测试代码(pytest)
├── .env                      # 实际环境变量(含密钥,**绝对不上传Git**)
├── .env.example              # 环境变量模板(上传Git,给协作者参考)
├── .gitignore                # Git 忽略文件配置
├── requirements.txt          # 依赖清单
├── config.py                 # 项目配置文件(开发/测试/生产分离)
└── run.py                    # 启动脚本

💡 Tip: Beginners can manually create these folders and empty files first, and the content will be gradually filled in subsequent chapters. Keeping the structure clear is more important than anything else.


4. Application factory mode (Flask official recommendation)

Why use application factory?

  • 🧩 Supports multi-environment switching: Development, testing, and production configuration can be completed with one click
  • 🔁 Avoid circular import: For example, routes import db, db import app, etc.
  • 🧪 Easy to write tests: multiple independent test instances can be created

4.1 Step 1: Unified initialization extension

Newapp/extensions.py, instantiate all extensions first, not bind app for the time being:

# app/extensions.py
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager
from flask_migrate import Migrate
from flask_wtf.csrf import CSRFProtect

# 实例化所有扩展(不带app参数)
db = SQLAlchemy()
login_manager = LoginManager()
migrate = Migrate()
csrf = CSRFProtect()

# 配置 login_manager
login_manager.login_view = "auth.login"  # 未登录用户自动跳转到登录页
login_manager.login_message = "请先登录后再访问该页面哦~"
login_manager.login_message_category = "info"  # 消息提示的样式类(Bootstrap用)

4.2 Step 2: Apply factory function

Newapp/__init__.py,writecreate_app(config_name)function:

# app/__init__.py
from flask import Flask
from config import config
from app.extensions import db, login_manager, migrate, csrf

def create_app(config_name="development"):
    # 1. 创建 Flask 实例
    app = Flask(__name__)

    # 2. 加载对应环境的配置
    app.config.from_object(config[config_name])

    # 3. 绑定扩展到当前 app
    db.init_app(app)
    login_manager.init_app(app)
    migrate.init_app(app, db)
    csrf.init_app(app)

    # 4. 注册 Blueprint(路由拆分)
    from app.routes.main import main_bp
    from app.routes.auth import auth_bp
    app.register_blueprint(main_bp)
    app.register_blueprint(auth_bp, url_prefix="/auth")  # 加前缀避免路由冲突

    # 5. (可选)添加简单的错误处理

    # 6. 返回配置好的 app 实例
    return app

4.3 Step 3: Multiple environment configuration files

Newconfig.py, Separate development/test/production configuration:

# config.py
import os
from dotenv import load_dotenv

# 加载项目根目录的 .env 文件
load_dotenv()

class BaseConfig:
    """基础配置(所有环境共用)"""
    SECRET_KEY = os.getenv("SECRET_KEY", "dev-secret-key-123-CHANGE-ME-IN-PRODUCTION")
    SQLALCHEMY_TRACK_MODIFICATIONS = False  # 关闭不必要的数据库修改追踪
    UPLOAD_FOLDER = os.path.join(os.path.dirname(__file__), "app/static/uploads")

class DevelopmentConfig(BaseConfig):
    """开发环境配置"""
    DEBUG = True
    SQLALCHEMY_DATABASE_URI = os.getenv("DEV_DATABASE_URL", "sqlite:///daoman_dev.db")

class ProductionConfig(BaseConfig):
    """生产环境配置"""
    DEBUG = False
    SQLALCHEMY_DATABASE_URI = os.getenv("PROD_DATABASE_URL", "postgresql://user:pass@localhost/daoman_prod")

class TestingConfig(BaseConfig):
    """测试环境配置"""
    TESTING = True
    SQLALCHEMY_DATABASE_URI = "sqlite:///:memory:"  # 内存数据库,测试完自动清空
    WTF_CSRF_ENABLED = False  # 测试时关闭 CSRF 验证

# 配置字典,供 create_app() 调用
config = {
    "development": DevelopmentConfig,
    "production": ProductionConfig,
    "testing": TestingConfig,
    "default": DevelopmentConfig,
}

4.4 Step 4: Start the script

Newrun.py, as the entry file of the project:

# run.py
import os
from app import create_app

# 从环境变量读取配置名,默认用开发环境
config_name = os.getenv("FLASK_ENV", "development")
app = create_app(config_name)

if __name__ == "__main__":
    app.run(
        host="127.0.0.1",  # 本地访问
        port=5000,
        debug=config[config_name].DEBUG,  # 同步环境的DEBUG配置
    )

5. Sensitive information and Git specifications

5.1 Use .env to manage keys and sensitive configurations

Write the key directly inconfig.pyIt is very dangerous and will be uploaded to Git and made public! Must usepython-dotenvLoad external.envdocument.

The difference between .env and .env.example

# .env(实际使用的,**绝对不能上传 Git**)
SECRET_KEY=230894d23c892b123a123d789f123a456c789d01
DEV_DATABASE_URL=sqlite:///daoman_dev.db
PROD_DATABASE_URL=postgresql://daoman_user:daoman_pass123@127.0.0.1:5432/daoman_prod
FLASK_ENV=development
# .env.example(模板,**必须上传 Git**,给协作者参考)
SECRET_KEY=change-this-to-a-strong-random-secret
DEV_DATABASE_URL=sqlite:///daoman_dev.db
PROD_DATABASE_URL=postgresql://user:pass@localhost/db_name
FLASK_ENV=development

5.2 .gitignore configuration

New.gitignore, ignore all unnecessary files:

# .gitignore
# 虚拟环境
venv/
.venv/

# Python 缓存
__pycache__/
*.py[cod]
*$py.class

# 数据库
*.db
*.sqlite
*.sqlite3

# 环境变量
.env
.env.local

# IDE 配置
.vscode/
.idea/
*.swp
*.swo

# 上传文件(可选)
app/static/uploads/*
!app/static/uploads/.gitkeep  # 保留空文件夹到Git

# 测试覆盖率
.coverage
htmlcov/

6. Summary and verification checklist

6.1 Project initialization quick verification checklist

Now you should have completed all the steps, follow the checklist below to verify success:

✅ 虚拟环境已创建并激活
✅ requirements.txt 已生成或手动编写
✅ 标准目录结构已建立
✅ 应用工厂模式已实现
✅ .env 和 .env.example 已创建
✅ .gitignore 已配置
✅ 运行 python run.py 能正常启动(访问 http://127.0.0.1:5000 会显示404,因为还没写首页路由~)

6.2 Best Practice Reminder

💡 Blackboard: Use Virtual Environments and App Factory Pattern from day one! Although minimal applications (e.g.hello.py) can be written directly in a file, but developing standardized habits can avoid the huge pain of refactoring later.


🔗 Extended reading