OK,OK,大家好,欢迎大家来到大鹏 AI 教育,我是张大鹏。
这次我们不讲一个新页面。
我们复盘一个比页面更重要的问题:
Django Admin 到底是谁在用,它应该看到哪个范围的数据?
如意 CRM 是多租户系统。
最初做 Admin 首页时,我把业务端的组织会员关系带了进来。
系统从管理员的Profile推断唯一组织,再按组织展示指标。
没有Profile或同时属于多个组织时,首页数据就会消失。
这个实现看起来很“安全”,实际上混淆了两种后台。
真正需要修复的不是一条查询,而是 Django Admin 的架构边界。
一、从错误的单组织首页发现边界问题
问题始于一段合理但前提错误的多租户推断。
1.1 为什么看似安全的租户推断其实错位
错误思路并不复杂:CRM 是多租户系统,任何入口都应先得到当前组织。
但这句话漏掉了一个前提:不同入口面对的使用者并不相同。
先看两条路径的分叉点。
问题并不是有没有org,而是谁有权决定它。
为什么左侧越努力寻找“唯一组织”,越容易挡住合法管理员?
原因在于它使用了业务会员关系解释平台身份。
- 🔍业务入口:租户员工依靠组织上下文限制业务数据。
- 🧭平台入口:开发、运维和平台管理员维护全平台模型。
- 🧱身份来源:
Profile是 CRM 会员关系,不是 staff 身份。 - 🚫错误后果:缺少唯一会员关系时,合法平台数据被隐藏。
结合无Profile和多Profile场景,我的判断很明确。
左侧不是更严格,而是使用了错误的身份来源。
- 🎯边界结论:租户上下文只属于租户业务入口。
- 🧹修复行动:Admin 首页移除会员数量和唯一组织推断。
因此,“自动选择唯一组织”没有增加真正的安全性。
它只是把错误的身份模型放到了错误的入口里。
1.2 四个问题重新确认 Admin 边界
我把这次问题压缩成四个必须先回答的问题。
它们是使用者、入口、数据范围和信任级别。
这四个节点中,哪一个可以等到写完代码以后再补?
答案是一个也不能。
- 👤使用者:入口服务于开发、运维和平台管理员。
- 🚪访问入口:使用原生
/admin/,不是租户 CRM 工作台。 - 🌐数据范围:默认查看全平台,人可以主动筛选组织。
- 🛡️信任级别:Django staff 和模型权限定义平台信任。
我在返工中最明确的体会是,四问不是一份文档模板。
它是动手前的架构闸门。
- 🧠判断顺序:先完成边界分类,再讨论查询和布局。
- 🛑停止条件:答案不清楚时停止实现,不允许静默补全。
答案确定后,很多争论自然结束了。
Admin 不从Profile、request.org、JWT 或 API Key 推断租户。
它也不把跨组织指标伪装成某个组织的经营看板。
二、在三种后台边界方案中做选择
边界明确以后,才有资格比较具体方案。
2.1 单租户 Admin、平台 Admin 和物理拆分
围绕 Django Admin,我评估了三种方案。
三张卡片真正比较的是什么?
它们比较的是入口、数据范围和维护成本如何组合。
- 🏢单租户 Admin:登录后绑定一个组织,只显示组织数据。
- 🗺️平台 Admin:沿用
/admin/,全平台数据显式展示组织。 - 🧩物理拆分:新建平台入口和注册白名单,形成强隔离。
当前只有内部人员使用 Admin,也不计划建设第二套后台。
因此我选择中间方案,而不是把“隔离最强”当成“当前最好”。
- 🟢当前选择:将 Django Admin 定义为内部平台控制台。
- ⏳重选条件:租户管理员进入 Admin 时重新做架构决策。
单租户方案适合租户管理员直接使用 Admin 的产品。
这不是当前如意 CRM 的使用方式。
物理拆分边界最强,但超出了本次 Admin 优化范围。
2.2 为什么当前选择内部平台 Admin
最终选择保留/admin/,由 Django 权限控制平台操作。
这符合 Django 官方对 Admin 的定位。
官方将其描述为可信用户使用的、以模型为中心的内部管理工具。
当需求转向业务流程界面时,应编写自己的视图。
不应该把整个业务前端建立在 Admin 上。
https://docs.djangoproject.com/en/6.0/ref/contrib/admin/
- ⚙️原生能力:保留模型权限、CSRF、审计日志和成熟表单。
- 📌范围标签:首页明确标注“全平台数据”。
- 🔎主动筛选:管理员选择组织筛选,不被系统静默绑定。
- 🪶改造半径:使用浅层
AdminSite和ModelAdmin扩展。
这不是说多租户不重要。
而是把多租户控制放回正确的位置。
业务 API 与 PostgreSQL RLS 继续保护租户数据。
Django Admin 承担受信平台人员的内部治理任务。
三、把架构决策写进源码
文字定义边界以后,源码必须执行同一个答案。
3.1RuyiAdminSite不再推断当前组织
RuyiAdminSite.index()只构造平台首页上下文。
它不读取管理员的 CRM 会员关系。
defindex(self,request,extra_context=None):context={**(extra_contextor{})}context["ruyi_dashboard"]=build_admin_dashboard(request,self)returnsuper().index(request,extra_context=context)仪表盘依据 Django 模型权限决定指标和快捷入口是否可见。
统计查询本身面向全平台。
- 📊查看权限:指标根据对应的
view_model权限显示。 - ➕新增权限:快捷入口根据对应的
add_model权限显示。 - 🏷️范围声明:首页固定展示“全平台数据”标签。
- 🔐入口保护:非 staff 用户仍由原生 Admin 拒绝。
无会员关系、单会员关系和多会员关系不会产生三套 Admin 语义。
这正是平台边界稳定后的直接结果。
3.2 组织归属必须显式可见
平台范围不等于忽略组织归属。
管理员能跨组织操作时,org更应该成为显式信息。
判断节点分出的三条路径说明了什么?
组织信息要同时覆盖查看、筛选和写入。
- 👁️组织列:查看记录时立即识别它属于哪个组织。
- 🔦组织筛选:排查问题时主动缩小到指定组织。
- 🖊️组织表单:新增记录时明确选择记录归属。
我选择在RuyiAdminSite.register()阶段统一增强。
它覆盖所有已注册且含org字段的模型,又不侵入业务查询。
- 🧷实现结论:组织归属显式可见,但不自动过滤数据。
- 🧯例外规则:特殊模型必须记录理由并补充精确测试。
PlatformOrgAdminMixin补充组织列、筛选器和表单字段。
它不修改查询集,也不自动填入某个组织。
defget_list_display(self,request):display=tuple(super().get_list_display(request))returndisplayif"org"indisplayelse(*display,"org")Django 官方把ModelAdmin定义为模型在管理界面中的表示。
它提供列表、筛选器和表单字段等扩展点。
https://docs.djangoproject.com/en/6.0/ref/contrib/admin/#modeladmin-objects
- 🧾查看合同:所属组织始终进入模型列表信息。
- 🎛️检索合同:组织始终成为平台管理员的筛选维度。
- ✍️写入合同:新增和编辑表单必须暴露组织字段。
- 🧲统一注册:通用混入避免业务应用逐个遗漏组织信息。
四、让一次修复变成长期边界
源码可以修复当前问题,但不能独自约束未来行为。
4.1 为什么只改源码还不够
项目过去没有清晰规则说明 Admin 不是租户工作台。
如果只删除一次查询,同样的推断仍可能在下一次优化中回来。
因此,我把决策落到了五个层次。
闭环里最关键的是哪一个节点?
关键不是单点,而是测试能否重新连接到最初的架构理由。
- 📜架构决策:记录选择理由、影响范围和重新决策条件。
- 🤖行为规范:
AGENTS.md在 Admin 改动前设置四问闸门。 - 🧰专用技能:
ruyi-admin-boundary给出禁区和验收命令。 - 🧬源码约束:统一
AdminSite与混入类执行平台边界。 - ✅自动测试:无会员、多会员和组织字段合同阻止回归。
我不再把“已经解释过”当成长期保障。
只有决策可执行、执行可验证,边界才真正进入项目。
- 🔁治理结论:规范、源码和测试必须首尾相接。
- 📍后续行动:每次 Admin 改动从四问开始,以测试结束。
文档解释“为什么”,规范约束“开始之前”。
源码执行“现在”,测试阻止“以后退回去”。
4.2 验收结果和适用边界
本次聚焦测试共通过 16 项。
迁移漂移检查没有发现新迁移。
文档治理检查和差异检查也通过。
- 🧪无会员场景:无
Profile的超级管理员可使用平台首页。 - 🔄多会员场景:多组织会员关系不会隐藏或改变指标。
- 🌍跨组织统计:两个组织的数据进入全平台指标。
- ⛔非 staff 场景:普通登录用户不能进入 Django Admin。
- 🗂️组织字段合同:带
org的模型暴露列、筛选和表单。 - 🌏三语言行为:简体中文、繁体中文和英语测试继续通过。
这些结果证明 Admin 平台边界和组织显式化合同成立。
它们不代表业务端租户隔离的全部安全性已经被重新验收。
这次没有扩展 SvelteKit、Flutter、业务 API 或 PostgreSQL RLS。
也没有把桌面端和移动端业务验收混入 Django Admin 范围。
边界修复既要知道必须改什么,也要知道不该顺手改什么。
如果未来租户管理员真的需要使用 Django Admin,
项目应新建架构决策,设计独立入口、组织选择和权限模型。
不能在当前平台 Admin 中恢复自动租户推断。
这次复盘留下的原则很简单:多租户入口都要有数据边界。
但边界不能靠猜。
先确认使用者、入口、数据范围和信任级别,
再让代码执行这个答案。