Django与Flask双框架搭建堂食餐厅上菜与预订系统

发布时间:2026/10/14 23:30:11
Django与Flask双框架搭建堂食餐厅上菜与预订系统 在技术群里经常能看到“Django 和 Flask 到底选哪个”的争论我一般懒得参与因为在实际项目里这个问题很多时候不是二选一而是两个都要用。这套堂食餐厅上菜与餐桌预订系统就是这么一个典型的例子后端主体用 Django 做管理后台和数据模型门店终端的高频小接口交给 Flask两个框架在一个项目里各管一摊配合得非常舒服。如果你正在做一个中小型餐饮管理系统或者正在纠结 Django 和 Flask 怎么分工这篇文章应该能给你一个比较完整的参考答案。这个项目围绕两个核心业务展开一是餐桌预订顾客通过小程序或前台登记预订桌台、到店核销二是堂食上菜从顾客点餐、后厨制作到服务员上菜每一步的状态都要在服务端流转清楚。整套系统用 Python 实现Django 负责较重的事务型业务和后台管理Flask 负责门店 iPad 点餐端、后厨大屏端的高频轻量请求。我会从架构拆解、数据库设计、接口实现、联调部署讲到实际踩过的坑尽量把能直接落地的细节都摆出来。1. 双框架的职责切分为什么同一套系统里同时用 Django 和 Flask很多人一听“Django 和 Flask 同时用”第一反应是“炫技”或者“复杂度失控”。但餐饮门店的场景比较特殊两个框架解决的是完全不同量级的问题。我的做法不是把两个框架缝合在一起而是让它们各自承担最擅长的那部分工作。1.1 Django 管“重”业务后台、权限、数据模型Django 最值钱的部分是自带的那套东西ORM、Admin 后台、迁移机制、用户权限体系。对于餐厅这种业务后台要管理的东西非常多菜品库、桌台信息、预订记录、订单流水、员工账号。如果这些全部用 Flask 从零搭工作量会大得离谱而且很容易在权限和事务处理上留下窟窿。我用 Django 管理四个核心模型Dish菜品、Table桌台、Reservation预订记录、Order订单及明细。Admin 后台直接开放给餐厅店长用他们可以在后台维护菜品价格、查看实时订单、调整桌台状态。Django 的 Admin 几乎是零成本解决“管理界面”的需求没必要重写一套。1.2 Flask 管“轻”接口门店终端的高频小请求门店终端的请求特点是高频、单次逻辑简单、要求响应快。比如服务员在 iPad 上点“起菜”、后厨大屏轮询新订单、顾客扫码查看订单进度这些请求每次做的事情都很轻但并发量集中在饭点对吞吐量有一定要求。Flask 的灵活和轻量恰好适合这种场景我不需要 Admin、不需要复杂中间件只需要把路由写清楚返回 JSON就够了。选择 Flask 还有一层实际考虑门店终端和后厨大屏通常部署在局域网里网络环境不如云端稳定接口要保持短小精悍出问题时要能快速定位。Flask 的代码路径短出错时看堆栈非常直观不像 Django 中间件链那么长。1.3 二者协作的主线统一数据库 HTTP 接口通信两个框架共享同一个 MySQL 数据库Django 建表、Flask 只读写这是协作的基础。在服务层面Flask 不直接改 Django 的模型代码而是通过 HTTP 调用 Django 提供的内部 API 来完成一些跨业务的操作比如创建订单后通知 Django 侧更新预订状态。这里有人可能会问既然 Flask 也能直接操作数据库为什么还要绕一圈 HTTP因为业务状态的一致性最好由一个服务端来把控如果两个服务都去写同一张表很容易出现更新逻辑分散、状态互相覆盖的问题。我的约定是Flask 负责“读”和“写操作入口”所有需要事务保证的状态变更统一走 Django 暴露的内部接口。这样代码里只有一条状态变更路径排查问题的时候思路清晰很多。2. 动手前必须想清楚的数据建模与业务状态机餐饮系统的难点不在接口多而在状态多。一张桌子从空闲到客人离开中间要经历“已预订”“已入座”“点餐中”“用餐中”“待清理”好几个状态任何一个环节衔接不好前台和后厨看到的数据就会对不上。所以建模时第一优先级不是表多不多而是状态机清不清楚。2.1 餐桌与预订这两张核心表怎么设计先看最简单的桌台模型。Table 表字段不多桌号、座位数、所在区域、当前状态。真正要注意的是桌号不要用自增 ID而是用业务上能识别的编码比如“A01”“B12”这样门店员工看到数据就能直接对应到实体桌子不用查字典。预订表是这套系统的核心之一。我的 Reservation 表包含预订人姓名、手机号、预订人数、期望到店时间、预订桌台、状态待确认/已确认/已到店/已取消/已过期。这里有一个特别容易踩的坑预订的“到店时间”不要只存日期要精确到分钟因为翻台排期要按小时段去算。另外一个预订可能对应多张桌子比如十人聚餐需要拼桌所以我在设计上让 Reservation 和 Table 是多对多关系而不是简单的外键指向一张桌。2.2 订单与上菜状态机点餐、出餐、上菜三态流转上菜流程的核心是一张 Order 表和一张 OrderItem 表。Order 记录的是这次开台的整体订单OrderItem 记录每道菜的分量、口味备注、当前状态。每道菜的状态流转是这套系统最值得花时间设计的部分已下单 → 已接单后厨开始做→ 已出餐菜从出餐口端出→ 已上菜服务员送到桌。这里必须单独解释一下为什么要把每道菜的状态拆这么细。客人加菜、退菜、催菜都是针对单道菜而不是整单的如果只在 Order 上存一个总状态后厨根本不知道哪道菜做到什么程度。我把 OrderItem 的状态当作主状态线Order 的总状态进行中/已完结只是所有菜品状态归集后的结果。2.3 为什么把“上菜记录”单独拆一张表按常规思路OrderItem 上加一个“是否已上菜”字段就够用了。但实际运营中服务员需要知道“这道菜是谁上的、什么时候上的、上了之后客人有没有催”。所以我加了一张 ServeRecord 表关联订单明细、上菜服务员 ID、上菜时间、备注。这张表让每个操作都有据可查店里出现菜品纠纷时可以直接翻记录。顺带说一句这套设计在数据库层面并不复杂关键是把“状态机”理解清楚。我在代码里建了一个状态流转表明确标注每个状态合法迁移到哪些状态非法迁移直接抛异常。比如“已出餐”不能直接跳回“已下单”“已上菜”不能在“已出餐”之前出现。这个约束看起来死板但在高峰期多个服务员同时操作时它能拦住绝大多数的人为失误。3. 餐桌预订模块从查桌、锁桌到核销的完整链路餐桌预订这个模块看起来简单实际上坑全藏在并发和超时处理里。预订的完整闭环是顾客选时间查可用桌台 → 提交预订锁定桌台 → 到店核销入座 → 用餐结束释放桌台。任何一步不做边界处理都会出现同一张桌子被两个顾客同时预订的尴尬。3.1 查询可用桌台与时段冲突判断查可用桌台不能只看“当前是否为空闲状态”而要看“指定时间段内是否被占用”。我设计了一个时段时间段冲突查询给定期望到店时间 start_time 和用餐时长默认 90 分钟查这个时间段内有没有已确认或已到店的预订记录占用这张桌。这个冲突判断我在 Vue 端和 Flask 端各做了一次但服务端是最终裁决者。Flask 的查询接口大概是这个思路def get_available_tables(start_time, duration90): end_time start_time timedelta(minutesduration) occupied_ids ( db.session.query(ReservationTable.table_id) .join(Reservation, Reservation.id ReservationTable.reservation_id) .filter( Reservation.status.in_([confirmed, seated]), Reservation.arrive_time end_time, func.date_add(Reservation.arrive_time, interval90 minute) start_time ) .distinct() ) return Table.query.filter(~Table.id.in_(occupied_ids), Table.status idle).all()注意这里用的是“存在交集即占用”的判断逻辑不是简单比对某一时刻两个预订只要时间段重叠就必须排除。3.2 到店核销与超时释放策略顾客到店后前台在系统里点“核销”预订状态从“已确认”变为“已到店”同时桌台状态从“已预订”变为“已入座”。这个操作必须在一个事务里完成否则会出现预订核销了但桌子没换状态的中间态。比较常被忽略的是超时释放。顾客预订了 12 点的桌子结果 13 点才到后一个预订 12:30 的客人也到了大家都在等这张桌子这时候怎么办我的策略是预订保留 20 分钟超时后自动释放桌台、状态变为“已过期”。释放动作由一个定时任务扫描完成每 5 分钟跑一次把超时预订标记为过期同时把对应桌台改回空闲。这个策略提前和店长确认过订了不来的客人会让餐厅损失翻台率必须用规则兜底。3.3 取消、改签与并发的幂等处理顾客取消预订直接改状态很简单麻烦的是“取消的同时另一个人正在预订这张桌子”的并发场景。我在桌台上加了一个乐观锁字段 version任何状态变更都要带上 version 条件更新更新成功则 version 1失败则说明数据已被其他请求改过返回冲突提示让前台刷新重试。幂等性也是预订接口的老大难问题。顾客手滑点了两次“提交预订”如果接口不做幂等处理就会出现两条重复预订。我的做法是前端生成一个 request_id 随请求带上服务端在 Reservation 表上对 (table_id, arrive_time, request_id) 建唯一索引重复请求直接命中唯一约束返回已有记录而不是新建。这个方法简单可靠比用 Redis 分布式锁省事得多。4. 堂食上菜流程点餐、后厨、服务员三端的状态同步上菜流程比预订模块复杂因为它涉及三方角色顾客/服务员点餐、后厨做菜、服务员上菜。三个角色操作的是同一批数据饭点高峰期并发达到几十甚至上百状态错乱是最大的风险点。4.1 点餐终端的请求设计与后厨接单服务员在 iPad 上勾选菜品、选择口味比如“辣度中辣”“不要香菜”后提交Flask 接收请求创建 Order 和 OrderItem初始状态都是“已下单”。之后后厨大屏会轮询拉取所有“已下单”的菜品厨师看到后点击“接单”菜品状态变为“已接单”。这里的设计决策是后厨端不做实时推送而是用 3 秒轮询。有人可能觉得用 WebSocket 更高级但实际门店的后厨大屏经常长时间没人操作轮询足够满足需求而且实现和排错成本低得多。轮询接口返回后厨需要关注的所有菜品和桌号后厨按顺序做菜即可。4.2 出餐通知与上菜确认的时序问题菜品做好后厨师在大屏上点“出餐”状态变为“已出餐”。这时传菜员或服务员需要收到通知知道哪桌的哪道菜可以上了。这个通知我用了一个很朴素的方案出餐记录写入 ServeRecord 表此时先占一行字段 serve_time 为空然后服务员在终端上轮询“已出餐未上菜”的数据服务员端确认“已上菜”时回填 ServeRecord 的 serve_time。为什么出餐时不直接改状态等上菜后再写入 ServeRecord因为传菜员需要知道“有哪些菜在出餐口等着”这个查询对应的是“已出餐且 ServeRecord 未回填时间”的菜品。如果把 ServeRecord 完全延后到上菜时才创建出餐口等待区就没有数据可查了。提前创建、延后回填是这套流程里比较关键的小细节。4.3 订单完结与异常菜品处理一张桌的客人全部上完菜后服务员在终端确认“订单完结”Order 状态改为“已完结”。但有个非常实际的情况某道菜做了一半发现食材没有了需要退菜。这时后厨不能直接删除菜品只能把 OrderItem 状态改成“已退菜”并且必须填写退菜原因。退菜操作同时要做两件事更新菜品状态、把退菜记录写进日结报表。这种操作不能让服务员在终端上自由发起要限制在后厨端和前台管理端防止乱退。高并发下最容易出错的地方是多个服务员同时修改同一批菜品状态。我在 OrderItem 的每一次状态更新里都加了条件WHERE id ? AND status 期望的当前状态相当于乐观锁的变体。这么做的效果非常明显高峰期基本没有再出现过“A 服务员把 B 服务员刚上完的菜又上了一遍”的重复操作。5. 双框架联调与部署落地Nginx 路由、日志与统一入口开发环境里 Django 跑在 8000 端口、Flask 跑在 5000 端口各自调各自的没问题。但生产环境必须统一入口不能让门店员工记住两个端口更不能用两个不同域名。这部分的落地经验可能比写得顺滑的接口代码更值钱。5.1 统一入口与反向代理配置我用 Nginx 做反向代理按路径把请求分流所有 /admin/ 和 /api/django/ 开头的外部请求转发到 Django 服务所有 /api/pos/、/api/kitchen/ 开头的请求转发到 Flask 服务。这样对外就是一个服务器地址门店终端只需要配置一个 baseURL。一个关键配置是把同源策略处理好。Flask 和 Django 如果返回不同的 CORS 头门店终端会突然在某些接口上报跨域错误。我的做法是在 Nginx 这一层统一加上允许的域名响应头后端两个框架都不再单独配置 CORS所有跨域问题在代理层一次性解决。5.2 两个服务的进程管理与日志归集Django 用 Gunicorn 跑 4 个 workerFlask 用 Gunicorn 跑 8 个 worker因为它的请求更轻更频需要更多并发进程。两个服务各自写各自的日志文件Django 的日志关注后台操作、权限变更、订单创建Flask 的日志关注门店终端的每次请求参数和响应状态。日志归集这块我踩过一个坑一开始两个服务各写各的日志线上出了问题要在两个文件里来回翻。后来我把所有业务关键操作统一打了一套“请求轨迹日志”包含 request_id、终端类型、操作人、操作对象、变更前后状态这五个字段无论是 Django 还是 Flask 都按这个格式写入同一个日志文件。这样排查问题时只需要按 request_id 拉出整条链路非常直观。5.3 部署脚本与环境隔离门店不止一家每家门店的数据库是同一套不同门店用 store_id 区分但服务可以部署在同一台服务器上。我的部署脚本分三步先从 Git 拉代码然后分别安装 Django 和 Flask 的依赖最后用 systemd 管理两个 Gunicorn 进程确保任何进程挂了都能自动拉起。这里要特别强调虚拟环境隔离。Django 和 Flask 的依赖包理论上是能共存于同一个 Python 环境的但为了避免某个包升级影响另一个框架我还是给两个服务各建了一个独立的 venv。两个服务不在同一个虚拟环境里彻底杜绝了依赖冲突的可能性。6. 实际运行中踩过的坑与排查链路复盘这一部分我想重点说说上线初期遇到的两个很典型的坑都不是什么“高级故障”而是逻辑边界没有考虑到位导致的低级问题但排查过程挺有代表性。6.1 并发订同一张桌乐观锁怎么救场第一个问题是重复预订。上线第三天前台反馈说偶尔会有两个顾客在不同渠道一个在小程序、一个在电话同时预订到同一张桌子。查日志发现两个请求恰好同一秒各查了一次桌台状态都看到“空闲”然后分别插入了一条预订记录其中一个就把另一个覆盖了。找到根因后加乐观锁也就是前面提到的 version 字段。具体 SQL 是更新 Table 时带上 version 条件UPDATE table SET status reserved, version version 1 WHERE id 1 AND version 5;如果更新影响行数为 0说明版本已经变化立刻回滚当前事务并返回“桌台已被占用”的提示。这个改动半小时就完成了但解决得非常彻底之后再没有出现过重桌问题。6.2 上菜状态乱序谁先动的手第二个问题更隐蔽。有一天店长反映某个服务员上菜时iPad 上显示“菜品已上”但后厨大屏上这道菜的状态却停在“已出餐”两边数据对不上。我查了数据库发现 OrderItem 的状态确实还是“已出餐”但 ServeRecord 里已经有一条时间记录。也就是说上菜确认这个动作执行了但菜品状态更新失败了。定位到原因服务员在上菜确认时前端先调用一个接口写入 ServeRecord再调用另一个接口更新 OrderItem 状态。第二个接口执行时系统判断当前状态不是“已出餐”拒绝更新。实际上是因为后厨那边在出餐后又做了一次“催菜更新”把状态动了一下导致状态校验过不去。我的修复方式是把两个接口合并成一个事务性操作写入 ServeRecord 和更新 OrderItem 状态放在同一个 Flask 接口里用事务包住要么都成功要么都回滚。同时在更新时不再强制判断“当前状态必须是已出餐”而是判断“当前状态不是已上菜和已退菜即可”扩大了合法迁移范围避免被中间态卡住。这个问题也让我意识到前后端接口拆得太碎在状态机严格的项目里会加倍放大错误概率。6.3 两个服务共用数据库时的锁与事务边界还有一个让我记住教训的问题Django 和 Flask 共用同一个数据库但两边的事务隔离级别默认并不一致。有一次 Flask 侧查询订单列表时发现总金额不对一查发现是 Django 后台正在批量修改菜品价格Flask 的普通读请求读到了中间态数据。这个问题的本质是 REPEATABLE READ 隔离级别下并发更新会导致某些查询读到快照数据。我没有去强制统一隔离级别而是在 Django 的批量修改接口上加了独占事务同时把价格修改安排在低峰期执行。对餐饮系统来说业务上允许短暂的数据延迟但不能允许数据错乱。想清楚这条底线技术上就不必过度设计。最后聊一点体会做这套系统花的时间比我预想的多大部分精力不是花在写接口上而是花在梳理状态流转的边界条件上。餐桌预订系统最难的不是数据库设计而是并发时谁先谁后的裁决逻辑上菜系统最复杂的不是接口数量而是三端操作下状态一致性的保障。这些问题的解法没有太多花哨技术就是乐观锁、事务、状态校验这类基本功但把它们落实到每一个关键接口里系统的稳定性就会完全不一样。如果你准备给餐厅做类似的系统我建议第一版先别上微服务、别上消息队列、别上 WebSocket把 Django 和 Flask 的职责切清楚把状态机建模提前想透这套组合已经能覆盖绝大多数堂食场景的稳定性要求。等到门店数据量真正上来、跨店管理需求出现时再考虑拆分也不迟。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询