恒美微站 Logo 恒美微站
  • 首页
  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心
  • 联系我们

零基础入门python21:Flask账本的数据模型——用户、分类和账目

  • 首页
  • 资讯中心
  • /
  • 零基础入门python21:Flask账本的数据模型——用户、分类和账目

相关资讯

Agent Router 一个免费用codex,claude的网站如何接入 CC Switch 2026/8/25 13:04:50
电商美工PS培训零基础怎么学 ?学设计就业推荐橡果教育 2026/8/25 13:04:50
蓝速科技丨新旧楼宇混杂场景下会议室预约门牌屏一体化改造方案 2026/8/25 13:04:50

最新资讯

摸鱼低代码任务调度中心入门教程:如何快速创建定时任务并查看执行日志
Vue 3项目中实现H.265视频流播放:MSE+WebAssembly方案全解析
2026年腾讯云轻量服务器10分钟部署WordPress全攻略
用QClaw与Obsidian构建AI驱动的动态知识库:从静态笔记到智能第二大脑
Odoo模块继承与视图扩展:从概念到实战的二次开发指南
AI Agent 面试题 353:A2A协议的服务发现和注册机制详解

今日推荐

三步把QQ空间历史说说导出到本地:GetQzonehistory 极简指南
洛谷 P7912:[CSP-J 2021 T4] 小熊的果篮 ← 双向链表
Transformers.js 网页端图像抠图实战:零后端 3 行代码返回透明 PNG

本周热门

Nextcloud 桌面客户端:把同步交给它,你只管改文件
如何将 HTML 转成 Word 文档且格式不丢失?html-to-docx 使用教程
Anki 批量操作卡片完整指南:一次搞定上千张,不再逐张修改

本月精选

如何用DamaiHelper实现演唱会门票的智能自动化抢购:完整技术解决方案指南
第4篇:59 倍性能差距的索引瓶颈定位——一次教科书级的全表扫描调优
终极歌词批量下载神器:5分钟解决离线音乐库歌词同步难题

零基础入门python21:Flask账本的数据模型——用户、分类和账目

发布时间:2026/8/25 13:04:50
零基础入门python21:Flask账本的数据模型——用户、分类和账目 零基础入门python21Flask账本的数据模型——用户、分类和账目一、上一篇课后练习讲解上一篇要求使用环境变量并验证测试配置。参考测试通过create_app({TESTING: True, SQLALCHEMY_DATABASE_URI: sqlite:///:memory:})创建临时应用再在应用上下文中建表。生产配置永远不应写进测试文件。上一篇课后练习完整答案上一篇练习的要求已落实到下面完整文件先运行项目测试再用 curl 对照状态码和数据库持久化结果答案要点Config 与 TestConfig 分离环境变量只在配置层读取测试使用内存 SQLite蓝图和扩展仍由 create_app 装配。文件app/config.py完整参考答案文件完整文件app/config.pyimportosclassConfig:APP_VERSIONos.getenv(APP_VERSION,0.1.0)SECRET_KEYos.getenv(SECRET_KEY,dev-only-change-me)SQLALCHEMY_DATABASE_URIos.getenv(DATABASE_URL,sqlite:///ledger.db)SQLALCHEMY_TRACK_MODIFICATIONSFalseclassTestConfig(Config):TESTINGTrueSQLALCHEMY_DATABASE_URIsqlite:///:memory:完整参考答案文件本篇对应的交付源码完整文件flask-ledger/app/init.pyfromflaskimportFlaskfrom.authimportbpasauth_bpfrom.extensionsimportdb,login_managerfrom.ledgerimportbpasledger_bpdefcreate_app(test_configNone):appFlask(__name__)app.config.from_mapping(SECRET_KEYdev-change-me,SQLALCHEMY_DATABASE_URIsqlite:///ledger.db,SQLALCHEMY_TRACK_MODIFICATIONSFalse,)iftest_config:app.config.update(test_config)db.init_app(app)login_manager.init_app(app)login_manager.unauthorized_handler(lambda:({message:请先登录},401))app.register_blueprint(auth_bp)app.register_blueprint(ledger_bp)app.get(/api/health)defhealth():return{status:ok}withapp.app_context():db.create_all()returnapp验收命令python -m pytest -qDjango 项目使用 python manage.py test。预期测试通过若失败先检查迁移、配置和事务回滚。二、本篇完成什么建立账本的三类数据用户拥有多个分类和账目账目必须属于一个分类。金额用Numeric(12, 2)不能用浮点数。三、模型代码classUser(UserMixin,db.Model):iddb.Column(db.Integer,primary_keyTrue)emaildb.Column(db.String(120),uniqueTrue,nullableFalse,indexTrue)password_hashdb.Column(db.String(255),nullableFalse)classCategory(db.Model):__table_args__(db.UniqueConstraint(user_id,name),)iddb.Column(db.Integer,primary_keyTrue)namedb.Column(db.String(40),nullableFalse)user_iddb.Column(db.Integer,db.ForeignKey(user.id),nullableFalse,indexTrue)classTransaction(db.Model):iddb.Column(db.Integer,primary_keyTrue)kinddb.Column(db.String(10),nullableFalse)amountdb.Column(db.Numeric(12,2),nullableFalse)user_iddb.Column(db.Integer,db.ForeignKey(user.id),nullableFalse,indexTrue)category_iddb.Column(db.Integer,db.ForeignKey(category.id),nullableFalse)联合唯一约束让同一用户不能创建两个同名分类它必须在数据库中实现因为两个并发请求可能同时通过 Python 的“先查询再新增”。外键保证账目不能指向不存在的用户或分类。四、迁移与验收教学项目先用db.create_all()初始化测试库生产项目应使用 Flask-Migrate 记录迁移。运行测试后检查负金额不会由模型自动变合法业务层还需要校验kind和金额范围。课后练习给 Transaction 增加happened_on日期字段并写一个按日期查询的测试。项目增量三个核心模型账本需要 User、Category、Entry 三个模型。Entry 的 user_id 是数据隔离关键金额使用 Numeric 而不是 Float日期字段用于月度报表。classEntry(db.Model):iddb.Column(db.Integer,primary_keyTrue)user_iddb.Column(db.ForeignKey(user.id),nullableFalse,indexTrue)category_iddb.Column(db.ForeignKey(category.id),nullableFalse)amountdb.Column(db.Numeric(12,2),nullableFalse)happened_ondb.Column(db.Date,nullableFalse)迁移后插入收入和支出查询时必须带当前用户过滤。课后练习增加数据库约束并验证非法金额。五、从业务名词到表结构先画关系再写模型账本不是“把一行 JSON 塞进数据库”。用户、分类、账目之间有明确关系一个用户拥有多条账目和分类一条账目可以没有分类但不能没有所属用户。把关系写成表格后再编码能够提前发现“删除用户时账目怎么办”这类问题。user (1) ─── category (0..n) │ └─────── entry (1..n) ─── category (0..1)对应的模型示例项目实际代码还包含时间字段和索引classEntry(db.Model):__tablename__entriesiddb.Column(db.Integer,primary_keyTrue)user_iddb.Column(db.Integer,db.ForeignKey(users.id),nullableFalse,indexTrue)category_iddb.Column(db.Integer,db.ForeignKey(categories.id),nullableTrue)amountdb.Column(db.Numeric(12,2),nullableFalse)happened_ondb.Column(db.Date,nullableFalse,indexTrue)notedb.Column(db.String(200),nullableFalse,default)Numeric(12, 2)表示最多 12 位、2 位小数金额不能用Float因为二进制浮点会出现0.1 0.2 ! 0.3的舍入误差。user_id加索引是因为之后的每一个列表查询都会按用户过滤。category_id允许为空表示用户暂时不分类但user_id必须存在这是领域规则而非界面约束。六、迁移和回滚的最小流程开发阶段可以使用db.create_all()但多人协作和生产发布必须使用迁移工具。每次模型改变都要记录“从哪个版本变到哪个版本”flask--app run.py db init# 只在项目第一次执行flask--app run.py db migrate-madd entry and categoryflask--app run.py db upgrade flask--app run.py db current执行migrate后一定要打开生成的迁移文件检查 SQL自动生成器不会理解“把字段改名”与“删掉旧字段”的业务含义。发布失败时使用flask db downgrade -1回退一个版本先恢复服务再分析迁移脚本。七、上一篇练习的实现和数据隔离同一用户的同名分类唯一约束必须包含user_id而不是只对name加全局唯一classCategory(db.Model):__table_args__(db.UniqueConstraint(user_id,name,nameuq_category_user_name),)API 查询也必须带用户条件Category.query.filter_by(idcategory_id, user_idcurrent_user.id).first_or_404()。只按id查出来再判断所有权虽然看似可行却很容易在另一个接口忘记判断把所有权放进 SQL 条件是更可靠的默认行为。八、验收输出与排错python-cfrom app import create_app; from app.extensions import db; appcreate_app({TESTING:True}); app.app_context().push(); print([t.name for t in db.metadata.sorted_tables])python-m pytest-q预期表名包含users、categories、entries。NoReferencedTableError表示外键字符串与__tablename__不一致UNIQUE constraint failed说明约束生效接口层应把它转成 409而不是返回 500。下篇会实现注册并把数据库异常转换为用户能看懂的错误。九、本篇练习写一个seed_demo()函数创建一个用户、两个分类和三条账目函数重复执行不得产生重复数据。要求使用事务失败时回滚。下一篇开始注册接口会复用这个事务边界。本篇结束完整模块文件下面是交付项目中真实存在的完整文件 flask-ledger/app/models.py。它覆盖本篇新增逻辑以及前文已经完成的依赖代码复制单个函数会丢失上下文因此这里提供整份文件。fromdatetimeimportdate,datetime,timezonefromdecimalimportDecimalfromflask_loginimportUserMixinfromwerkzeug.securityimportcheck_password_hash,generate_password_hashfrom.extensionsimportdb,login_managerclassUser(UserMixin,db.Model):iddb.Column(db.Integer,primary_keyTrue)emaildb.Column(db.String(120),uniqueTrue,nullableFalse,indexTrue)password_hashdb.Column(db.String(255),nullableFalse)created_atdb.Column(db.DateTime,defaultlambda:datetime.now(timezone.utc))defset_password(self,password:str)-None:self.password_hashgenerate_password_hash(password)defcheck_password(self,password:str)-bool:returncheck_password_hash(self.password_hash,password)login_manager.user_loaderdefload_user(user_id:str):returndb.session.get(User,int(user_id))classCategory(db.Model):__table_args__(db.UniqueConstraint(user_id,name),)iddb.Column(db.Integer,primary_keyTrue)namedb.Column(db.String(40),nullableFalse)user_iddb.Column(db.Integer,db.ForeignKey(user.id),nullableFalse,indexTrue)classTransaction(db.Model):iddb.Column(db.Integer,primary_keyTrue)kinddb.Column(db.String(10),nullableFalse)amountdb.Column(db.Numeric(12,2),nullableFalse)notedb.Column(db.String(200),default)happened_ondb.Column(db.Date,defaultdate.today,nullableFalse,indexTrue)user_iddb.Column(db.Integer,db.ForeignKey(user.id),nullableFalse,indexTrue)category_iddb.Column(db.Integer,db.ForeignKey(category.id),nullableFalse)defto_dict(self)-dict:return{id:self.id,kind:self.kind,amount:str(Decimal(self.amount).quantize(Decimal(0.01))),note:self.note,happened_on:self.happened_on.isoformat(),category_id:self.category_id,}

关于恒美微站

恒美微站专注于为个体商户、工作室提供极简自助建站服务,让每个人都能轻松拥有专业网站。

快速链接

  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心

服务项目

  • 可视化建站
  • 拖拽编辑
  • 主题定制
  • SEO 优化
  • 网站托管

联系方式

  • 📍 地址:北京市朝阳区建国路 88 号
  • 📞 电话:400-888-8888
  • ✉️ 邮箱:info@hmyw.cn
  • 🕐 时间:周一至周日 9:00-18:00

© 2024 恒美微站 hmyw.cn 版权所有 | 京 ICP 备 12345678 号