使用Ruby Mongoid实现模型校验警告时warnings为空问题排查
这不是Mongoid的Bug,是实例生命周期和自定义校验逻辑的设计问题,核心原因如下:
- 你自定义的
@warnings是内存级的实例变量,仅在当前运行过校验回调的实例中存在,不会持久化到数据库 - 当校验过程中产生了
errors、校验失败时,不会触发数据持久化和页面重定向,视图层使用的还是提交表单时的同一个实例,因此可以读取到@warnings的值 - 当没有产生
errors、校验通过时,通常会执行持久化操作,之后如果跳转页面,Helper中访问的实例是跳转后从数据库重新查询的新实例,没有运行过校验回调,@warnings还未初始化因此返回空;如果校验成功后手动调用了reload方法,也会清空内存中的@warnings实例变量。
方案1:仅需表单提交时展示警告(无需持久化)
重写valid?方法,确保每次校验都主动填充warnings,且在校验完成后保留内容:
class MyModel include Mongoid::Document validate :check_warnings def warnings @warnings ||= ActiveModel::Errors.new(self) end def valid?(context = nil) # 每次校验前清空旧警告 warnings.clear # 执行原有校验逻辑 super end def check_warnings warnings.add(:some_field, :not_exist) if some_field.blank? warnings.add(:another_field, :not_exist) if another_field.blank? end end
如果需要在校验成功的页面展示警告,可以将警告内容存入flash传递,或者直接使用当前实例渲染页面,不要重定向。
方案2:需要持久化警告,后续随时可查看
新增字段存储警告内容,在校验时自动同步:
class MyModel include Mongoid::Document # 新增字段存储校验警告 field :validation_warnings, type: Hash, default: {} validate :check_warnings def warnings @warnings ||= ActiveModel::Errors.new(self).tap do |err| # 从持久化字段恢复已有警告 validation_warnings.each do |attr, messages| messages.each { |msg| err.add(attr, msg) } end end end def check_warnings warnings.clear warnings.add(:some_field, :not_exist) if some_field.blank? warnings.add(:another_field, :not_exist) if another_field.blank? # 同步警告到持久化字段 self.validation_warnings = warnings.to_hash end end
使用该方案后,无论实例是否重新查询,都可以通过warnings方法读取到上次校验产生的警告内容。
内容的提问来源于stack exchange,提问作者TheR
相关产品推荐
相关产品推荐

