Rails模型保存前更新price字段计算总价的正确方法咨询
问题分析与解决方案
你的问题核心不是模型属性只读,而是赋值语句使用了错误的方法,导致price字段没有被正确赋值。
关键错误点
你在set_total中使用了printf('$%.2f',totprice)来格式化价格,但printf的作用是将内容输出到控制台(所以你在日志里看到了$135.00),它的返回值是输出的字符长度(比如$135.00是7个字符,返回7),而不是格式化后的字符串。把整数赋值给字符串类型的price字段,会导致ActiveRecord无法正确处理,最终price被置为nil,数据库保存时沿用了表单提交的初始值"0.00"。
修正步骤
1. 替换printf为sprintf
sprintf会返回格式化后的字符串,正好适合赋值给price字段:
self.price = sprintf('$%.2f', totprice)
2. 优化价格查询逻辑
你用find_by_sql的方式不够高效且不够Ruby化,改用ActiveRecord关联查询更安全简洁:
# 假设Price模型已经和Product模型建立了belongs_to :product关联 price_map = Price.joins(:product).pluck('products.name', 'prices.price').to_h.transform_values(&:to_f)
3. 修正拼写错误
你代码里的"Biographical Skectch"存在拼写错误,应该是"Biographical Sketch",否则无法匹配到对应的价格项。
修正后的完整set_total方法
private def set_total # 构建产品名称到价格的映射表 price_map = Price.joins(:product).pluck('products.name', 'prices.price').to_h.transform_values(&:to_f) totprice = price_map[selection] || 0.0 totprice += price_map["Certificate"] if cert totprice += price_map["Biographical Sketch"] if bio totprice += price_map["Photograph"] if pic # 正确格式化价格并赋值 self.price = sprintf('$%.2f', totprice) end
补充说明
before_save回调完全可以修改模型属性,不存在只读问题,之前的失效是因为赋值内容错误。- 不需要使用
after_save加手动SQL更新,这种方式会额外增加一次数据库查询,还可能引发事务一致性问题,用before_save正确赋值是最优方案。
内容的提问来源于stack exchange,提问作者user3754107
相关产品推荐
相关产品推荐

