打包上线 — 部署到 Railway
本章你将学会应用工厂模式、生产环境配置,并用 Railway 将 Flask 博客部署上线。
应用工厂模式 create_app()
到目前为止,app.py 中直接创建了 app = Flask(__name__),这在开发时够用,但有几个问题:
- 测试时无法创建独立的 app 实例
- 无法按环境(开发/测试/生产)切换不同配置
- 扩展初始化与 app 实例紧耦合
应用工厂模式 解决了这些问题:把 app 的创建封装到一个函数中。
实例
# 文件路径:app/__init__.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager
from flask_migrate import Migrate
from config import Config
db = SQLAlchemy()
login_manager = LoginManager()
migrate = Migrate()
def create_app(config_class=Config):
"""应用工厂:根据配置类创建 Flask 应用实例"""
app = Flask(__name__)
app.config.from_object(config_class)
# 初始化扩展(先创建 app 实例,再将扩展绑定到 app)
db.init_app(app)
login_manager.init_app(app)
migrate.init_app(app, db)
login_manager.login_view = 'auth.login'
login_manager.login_message = '请先登录后再访问。'
# 注册 Blueprint
from app.blueprints.main import main_bp
from app.blueprints.posts import posts_bp
from app.blueprints.auth import auth_bp
from app.blueprints.user import user_bp
app.register_blueprint(main_bp)
app.register_blueprint(posts_bp)
app.register_blueprint(auth_bp)
app.register_blueprint(user_bp)
# user_loader 需要在 app 存在后定义
from app.models import User
@login_manager.user_loader
def load_user(user_id):
return User.query.get(int(user_id))
return app
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager
from flask_migrate import Migrate
from config import Config
db = SQLAlchemy()
login_manager = LoginManager()
migrate = Migrate()
def create_app(config_class=Config):
"""应用工厂:根据配置类创建 Flask 应用实例"""
app = Flask(__name__)
app.config.from_object(config_class)
# 初始化扩展(先创建 app 实例,再将扩展绑定到 app)
db.init_app(app)
login_manager.init_app(app)
migrate.init_app(app, db)
login_manager.login_view = 'auth.login'
login_manager.login_message = '请先登录后再访问。'
# 注册 Blueprint
from app.blueprints.main import main_bp
from app.blueprints.posts import posts_bp
from app.blueprints.auth import auth_bp
from app.blueprints.user import user_bp
app.register_blueprint(main_bp)
app.register_blueprint(posts_bp)
app.register_blueprint(auth_bp)
app.register_blueprint(user_bp)
# user_loader 需要在 app 存在后定义
from app.models import User
@login_manager.user_loader
def load_user(user_id):
return User.query.get(int(user_id))
return app
配置文件 config.py
实例
# 文件路径:config.py
import os
from dotenv import load_dotenv
# 从 .env 文件加载环境变量(仅开发环境使用)
load_dotenv()
class Config:
SECRET_KEY = os.environ.get('SECRET_KEY', 'dev-secret-key')
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL', 'sqlite:///blog.db')
SQLALCHEMY_TRACK_MODIFICATIONS = False
class DevelopmentConfig(Config):
DEBUG = True
class ProductionConfig(Config):
DEBUG = False
# 根据环境变量选择配置
config_map = {
'development': DevelopmentConfig,
'production': ProductionConfig,
'default': DevelopmentConfig
}
import os
from dotenv import load_dotenv
# 从 .env 文件加载环境变量(仅开发环境使用)
load_dotenv()
class Config:
SECRET_KEY = os.environ.get('SECRET_KEY', 'dev-secret-key')
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL', 'sqlite:///blog.db')
SQLALCHEMY_TRACK_MODIFICATIONS = False
class DevelopmentConfig(Config):
DEBUG = True
class ProductionConfig(Config):
DEBUG = False
# 根据环境变量选择配置
config_map = {
'development': DevelopmentConfig,
'production': ProductionConfig,
'default': DevelopmentConfig
}
新的入口文件 wsgi.py
实例
# 文件路径:wsgi.py
from app import create_app
app = create_app()
from app import create_app
app = create_app()
现在启动方式变为:
(venv) $ flask --app wsgi run --debug # 开发模式 (venv) $ gunicorn wsgi:app # 生产模式(Gunicorn)
注意 import 顺序:Blueprint 和 user_loader 必须在 create_app() 内部导入(而非文件顶部),否则会触发循环导入(Blueprint 引用了 models,models 引用了 db,而 db 此时尚未绑定到 app)。
生成 requirements.txt 与 Procfile
(venv) $ pip freeze > requirements.txt
确保文件中包含关键依赖:Flask、Flask-SQLAlchemy、Flask-Migrate、Flask-Login、Flask-WTF、Flask-Admin、gunicorn、python-dotenv。
在项目根目录创建 Procfile:
web: gunicorn wsgi:app
将项目推送到 GitHub
$ git init $ echo "venv/" > .gitignore $ echo "__pycache__/" >> .gitignore $ echo "*.pyc" >> .gitignore $ echo "instance/" >> .gitignore $ echo ".env" >> .gitignore $ git add . $ git commit -m "初始化 Flask 博客项目" $ git branch -M main $ git remote add origin https://github.com/你的用户名/flask-blog.git $ git push -u origin main
务必把
.env加入.gitignore。.env 文件包含 SECRET_KEY 等敏感信息,一旦提交到 GitHub 就无法彻底删除(Git 历史会保留)。
Railway 部署
- 访问 railway.app,用 GitHub 登录
- New Project → Deploy from GitHub repo → 选择 flask-blog
- Railway 自动检测 Procfile,识别 Gunicorn 启动命令
- 在 Variables 中设置环境变量
- 点击 Deploy
环境变量配置
| 变量名 | 值 | 说明 |
|---|---|---|
| SECRET_KEY | 随机生成的长字符串 | 加密 Session 和 CSRF Token |
| DATABASE_URL | 由 Railway 自动注入 | 生产数据库(Railway 提供 PostgreSQL) |
| FLASK_ENV | production | 生产环境标识 |
快速生成 SECRET_KEY:
$ python -c "import secrets; print(secrets.token_urlsafe(50))"
部署成功后,获得链接如 https://flask-blog.up.railway.app。
下一步学习方向
| 学习方向 | 适合谁 | 推荐起点 |
|---|---|---|
| Flask REST API | 为前端框架(Vue3/React)构建 API | 用 jsonify 代替 render_template,构建 RESTful 接口 |
| Flask + Vue3/React 前后端分离 | 想用 Flask 做后端,Vue3/React 做前端 | Flask 提供 JSON API,前端 fetch 通信 |
| PostgreSQL | 需要生产级数据库 | 替换 SQLite,安装 psycopg2,修改 DATABASE_URL |
| Docker 部署 | 容器化部署 | 编写 Dockerfile,用 docker-compose 编排 |
