4. 项目初始化:用 Flask 搭骨架,配数据库,建 ORM 模型

好,咱们正式开始动手。上一章我们把需求理清了,这一章的目标很明确:让项目跑起来,能连上数据库,能操作数据表。

我个人习惯,做物联网项目时,先把基础设施夯实。你想想看,如果数据库连接配置错了,后面写再多业务代码也是白搭。所以这一章,咱们一步一个脚印来。

4.1 创建项目目录结构

先别急着写代码,把文件夹结构搭好。我见过不少新手,把所有文件都扔在根目录下,最后项目乱成一锅粥。

这是我推荐的结构,简洁且实用:

device_registry/
├── app.py              # 应用入口
├── config.py           # 配置文件
├── models.py           # ORM 模型
├── requirements.txt    # 依赖清单
└── venv/               # 虚拟环境(不提交到 Git)

嗯,这里要注意:venv 文件夹千万别提交到版本控制里。我吃过这个亏,有一次把虚拟环境整个推上去了,结果同事拉下来发现路径全不对,折腾了半天。

4.2 初始化 Flask 应用骨架

Flask 的初始化其实就几行代码。但我想强调一点:工厂模式。虽然小项目用简单模式也行,但为了后续扩展,我建议一开始就用工厂函数。

来看 app.py:

from flask import Flask
from config import Config

def create_app():
    app = Flask(__name__)
    app.config.from_object(Config)

    # 注册蓝图(后续章节会加)
    # from routes import device_bp
    # app.register_blueprint(device_bp)

    return app

if __name__ == '__main__':
    app = create_app()
    app.run(debug=True, host='0.0.0.0', port=5000)

为什么用 create_app?说白了,就是为了测试方便。你写单元测试时,可以创建多个应用实例,互不干扰。我在做设备管理平台时,就因为一开始没用工厂模式,后来加测试时重构了一遍,那叫一个痛苦。

4.3 配置数据库连接

接下来是 config.py。这里我选 SQLite 做演示,因为零配置,上手快。生产环境你换成 MySQL 或 PostgreSQL 就行,改个连接字符串的事。

import os

class Config:
    SECRET_KEY = os.environ.get('SECRET_KEY') or 'dev-secret-key'
    SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or \
        'sqlite:///device_registry.db'
    SQLALCHEMY_TRACK_MODIFICATIONS = False

这里有个坑:SQLALCHEMY_TRACK_MODIFICATIONS 一定要设为 False。为什么?因为 Flask-SQLAlchemy 的信号机制会消耗大量内存,而且你基本用不上。我曾经在一个项目中忘了关,结果内存占用飙到 2GB,查了半天才发现是这玩意儿在作祟。

⚠️ 避坑指南: 千万不要把数据库连接字符串硬编码在代码里。用环境变量或者配置文件,这是安全底线。我曾经见过有人把生产库密码直接写在 config.py 里,然后提交到了公开仓库……嗯,后果你懂的。

4.4 初始化 ORM 模型

ORM 模型是咱们和设备身份打交道的桥梁。先安装依赖:

# requirements.txt
Flask==2.3.3
Flask-SQLAlchemy==3.1.1

然后初始化数据库对象。在 models.py 里:

from flask_sqlalchemy import SQLAlchemy
from datetime import datetime

db = SQLAlchemy()

class DeviceIdentity(db.Model):
    __tablename__ = 'device_identity'

    id = db.Column(db.Integer, primary_key=True)
    device_id = db.Column(db.String(64), unique=True, nullable=False, index=True)
    device_secret = db.Column(db.String(128), nullable=False)
    status = db.Column(db.String(16), default='active')  # active / revoked
    created_at = db.Column(db.DateTime, default=datetime.utcnow)
    updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)

    def to_dict(self):
        return {
            'id': self.id,
            'device_id': self.device_id,
            'status': self.status,
            'created_at': self.created_at.isoformat(),
            'updated_at': self.updated_at.isoformat()
        }

这个模型就是咱们设备身份注册系统的核心。每个字段都有它的用途:

字段名 类型 说明
id Integer (PK) 自增主键,内部使用
device_id String(64) 设备唯一标识,对外暴露,加索引
device_secret String(128) 设备密钥,哈希存储,绝不存明文
status String(16) 状态:active 正常,revoked 吊销
created_at DateTime 创建时间,自动填充
updated_at DateTime 更新时间,自动更新
💡 我的经验: device_secret 字段长度设 128 位,是因为后续我们会用 SHA-256 哈希,输出正好 64 字节,十六进制表示就是 128 个字符。别设短了,否则存不下。

4.5 整合:让 Flask 认识数据库

现在要把 db 对象注册到 Flask 应用里。修改 app.py:

from flask import Flask
from config import Config
from models import db

def create_app():
    app = Flask(__name__)
    app.config.from_object(Config)

    db.init_app(app)

    # 创建所有表(开发环境用,生产环境建议用迁移工具)
    with app.app_context():
        db.create_all()

    return app

这里我用了 db.create_all(),它会在第一次运行时自动建表。但注意:这只是开发阶段的快捷方式。生产环境我强烈建议用 Flask-Migrate 做数据库迁移,否则改模型时你会哭的。

我曾经在项目上线后,需要给表加个字段,结果直接删表重建,把线上数据全丢了……嗯,从那以后我再也不敢在生产环境用 create_all 了。

4.6 验证:跑起来看看

启动应用:

python app.py

如果看到 Running on http://0.0.0.0:5000,说明骨架搭好了。再检查一下数据库文件:

ls -la instance/device_registry.db

有文件生成,说明数据库连接和建表都成功了。你可以用 SQLite 客户端看一眼:

sqlite3 instance/device_registry.db
.tables
.schema device_identity

应该能看到 device_identity 表,字段和咱们定义的一致。

4.7 本章知识体系总览

下面这张图,把咱们这一章做的事串起来了:

项目初始化核心流程 Flask 应用 create_app() 配置加载 config.py 数据库连接 SQLite / MySQL ORM 模型 DeviceIdentity 自动建表 db.create_all() 💡 关键点 • 工厂模式 create_app() 便于测试和扩展 • 数据库连接字符串通过环境变量配置,不硬编码 • ORM 模型定义字段时,长度和类型要精确匹配业务需求

4.8 本章小结

这一章我们做了三件事:

  • 用 Flask 搭好了项目骨架,采用工厂模式
  • 配置了数据库连接,用环境变量管理敏感信息
  • 定义了 DeviceIdentity ORM 模型,并成功建表