问卷建模

Odoo 活动问卷为什么不是把答案贴在报名表上:event.question、registration.answer 与 once_per_order 边界讲透

很多人以为 Odoo 活动问卷只是给报名表多加几个字段。实际上,event.question、event.question.answer、event.registration.answer 和事件上的 general / specific questions 共同组成了一套独立的数据模型。看懂它,才能理解为什么有些问题按订单问一次,有些问题却要对每个参会人分别作答。

Odoo 开发 其他
进阶 开发者 2 分钟阅读
0 评论 0 点赞 0 收藏 10 阅读

先说结论

Odoo 的活动问卷不是“在报名表上多挂几个文本框”,而是一套题目、候选答案、报名人答案、事件问题集合分层建模的结构。

核心模型至少有四个:

  • event.question:题目本体
  • event.question.answer:选择题候选项
  • event.registration.answer:报名人实际作答
  • event.event.question_ids:某场活动启用了哪些题目

再加上 once_per_order 这个字段,系统还能继续把题目拆成两类:

  • 针对整个订单只问一次
  • 针对每个参会人分别作答

所以这套设计真正解决的问题不是“怎么展示几个问题”,而是:

一场活动里的问题,哪些是共用信息,哪些是参会人级信息;题库能不能复用;答案录入后还能不能随便改模型。

第一层:为什么题目要独立成 event.question

event.question 不是某场活动里的临时字段,它本身就是独立模型。

字段设计已经说明了这一点:

  • title
  • question_type
  • event_type_ids
  • event_ids
  • is_default
  • is_reusable
  • answer_ids
  • once_per_order
  • is_mandatory_answer

这意味着 Odoo 不把问题当作一次性表单片段,而是当作可沉淀、可复用、可挂到事件模板和具体活动上的题库元素

所以一个问题完全可以:

  • 成为默认问题
  • 被多个 event type 复用
  • 被多个活动共享
  • 在选择题场景下拥有自己的候选答案列表

这比“活动表上直接加几个 JSON 配置”更重,但也更稳。

第二层:为什么答案不直接写回 event.registration 字段

很多初学者会本能地想:

  • 姓名、邮箱都在 event.registration
  • 那活动问卷答案是不是也顺手加几列就行

但官方没有这么做,而是专门建了 event.registration.answer

这个模型最关键的字段是:

  • question_id
  • registration_id
  • question_type
  • value_answer_id
  • value_text_box

它表达了一个很清楚的思想:

报名人对题目的作答,是一张独立事实表。

这样设计有几个明显好处:

  1. 一个报名人可以答很多题,不需要在报名表上无限加列
  2. 同一套题库可以复用于不同活动
  3. 选择题和文本题可以共用一张答案表,只是值落在不同字段
  4. 后续统计、透视、图表分析更自然

这也是为什么 action_view_question_answers() 能直接把问题答案转成分析视图,而不是先去拼一堆动态字段。

第三层:为什么选择题还要再拆一张 event.question.answer

如果题目是 simple_choice,系统不会把候选值直接写死在字符串里,而是再用 event.question.answer 存。

这张表非常轻,但作用很大:

  • name
  • question_id
  • sequence

它保证了选择题选项本身也有结构化身份,而不是纯文本。

这带来两个重要结果:

  • 可以稳定统计每个选项被选了多少次
  • 可以控制删除边界

event_question_answer.py_unlink_except_selected_answer() 明确限制:

  • 如果某个候选项已经被报名人选过,就不能删

这和 event.question.write() 里“不允许把已有人作答的题改 question_type”是一套思路:

一旦真实业务答案已经落库,题目结构就不能任性改。

第四层:once_per_order 为什么是这套建模里最值钱的字段之一

event.question.once_per_order 的 help 写得很直白:

  • 像 Company Name 这种问题,答案对同一订单里的所有人都一样
  • 这种题不需要对每个参会人都重复问

event.event 上,系统进一步把问题分成:

  • general_question_idsonce_per_order = True
  • specific_question_idsonce_per_order = False

这说明 Odoo 很清楚活动报名里存在两层信息:

订单级信息

比如:

  • 公司名
  • 开票抬头相关信息
  • 团队统一说明

参会人级信息

比如:

  • 姓名
  • 邮箱
  • 手机
  • 饮食偏好
  • 尺码
  • 个人问题答案

如果不区分这两层,团体报名时表单会非常笨重:

  • 同一个公司名要填 N 次
  • 用户体验差
  • 后台数据也显得重复

所以 once_per_order 不是小优化,而是报名问卷有没有业务感的关键。

第五层:为什么切换 event type 时,旧问题不会被粗暴清空

event.event._compute_question_ids() 很值得看。

当活动切换 event_type_id 时,系统不是简单把旧题全删、新题全覆盖,而是先保留:

  • 这场活动里已经有报名答案的题目

源码里用的是:

  • event.registration_ids.registration_answer_ids.question_id & self._origin.question_ids

也就是说,只要某个题已经被真实报名人回答过,它就会被列入 questions_tokeep_ids

这条规则非常稳。

因为一旦有人答过:

  • 这题已经成为历史数据结构的一部分
  • 直接删掉会让旧答案失去语义挂点
  • 后续统计和回溯都会变脏

所以 Odoo 选择的是:

  • 没有历史答案的题,可以随着模板切换而移除
  • 已有历史答案的题,优先保留

这不是保守,而是数据建模应该有的边界感。

第六层:为什么默认问题和可复用问题不是一回事

event.question 里还有两个容易混淆的概念:

  • is_default
  • is_reusable

同时模型约束里明确要求:

  • default question 必须 reusable

这意味着:

  • 默认问题:系统新建活动时默认带上
  • 可复用问题:以后还允许被其它活动继续选用

默认问题一定要可复用,因为默认配置本来就是为了反复用。

但可复用问题不一定非要是默认问题。

这是一个很典型的“默认集”和“可选题库”的区分。

最容易踩的 4 个坑

1)把活动问卷理解成动态字段拼装

不对。

官方设计更接近“问卷题库 + 答案事实表”。

2)已经有人作答后,还想改 question_type

源码明确禁止。

因为文本题改成选择题,或者反过来,都会让已有答案失去匹配关系。

3)删掉已经被选过的候选项

也不行。

因为历史答案还挂在 value_answer_id 上。

4)团体报名时忘了区分 once_per_order

结果就是同一类共用信息被迫重复填很多次,前台很难用,后台数据也很臃肿。

实战里怎么设计活动问题更合理

可以按下面这条线做:

放到 once_per_order = True 的问题

  • 公司名称
  • 团队统一联系人信息
  • 开票或组织级补充信息

放到 once_per_order = False 的问题

  • 每位参会人的称呼
  • 手机 / 邮箱
  • 餐饮偏好
  • 个人岗位、经验、尺码等

选择题优先用候选答案建模的场景

  • 你以后需要做统计
  • 你希望答案可排序
  • 你希望保留选项级历史一致性

排查“为什么问题切模板后还留着”的顺序

推荐这样看:

  1. 这个题是不是已经有 event.registration.answer
  2. 它是不是来自旧的 question_ids
  3. 它是不是默认问题或模板问题
  4. 它属于 general_question_ids 还是 specific_question_ids

很多人以为这是“缓存没清掉”,其实往往是源码故意保留历史题。

最后一句

Odoo 活动问卷设计得好的地方,不是能问问题,而是它把:

  • 题目复用
  • 选项结构化
  • 报名人答案落库
  • 订单级 / 参会人级边界
  • 历史答案保护

放进了同一套模型里。

所以真正需要理解的不是“表单怎么显示”,而是:

这个问题到底属于活动题库、订单信息,还是参会人级事实;一旦有人答过,哪些结构还能改,哪些已经不能乱动。

看懂这层,活动问卷就不再只是前端表单,而是一套非常像样的数据模型。

DISCUSSION

评论区

想参与讨论?先 登录 再发表评论。
还没有评论,你可以成为第一个留言的人。