覆写rails_admin默认功能时回调执行顺序异常引发Mongoid错误
问题:RailsAdmin覆写逻辑导致Mongoid类型不匹配错误
我们通过config/initializers/rails_admin_index_override.rb和config/initializers/rails_admin_edit_override.rb文件覆写RailsAdmin的默认功能,但rails_admin_edit_override.rb中的自定义Edit动作逻辑会在数据格式化回调之前执行,触发如下Mongoid类型错误:
Mongoid::Errors::InvalidValue ( message: Value of type String cannot be written to a field of type Array summary: Tried to set a value of type String to a field of type Array resolution: Verify if the value to be set correspond to field definition): config/initializers/rails_admin_edit_override.rb:22:in `block (2 levels) in <class:Edit>'
当前使用的覆写代码:
module RailsAdmin module Config module Actions class Edit < RailsAdmin::Config::Actions::Base register_instance_option :member do true end register_instance_option :http_methods do [:get, :put] end register_instance_option :controller do proc do if request.get? # EDIT respond_to do |format| format.html { render @action.template_name } format.js { render @action.template_name, layout: false } end elsif request.put? # UPDATE exclude_models = ['product', 'manufacturer', 'language_code', 'vip_influencer_video_note', 'video', 'celebrity', 'vertical'] sanitize_params_for!( request.xhr? ? :modal : :update ) unless exclude_models.include?(params[:model_name]) @object.assign_attributes(params[@abstract_model.param_key]) @authorization_adapter && @authorization_adapter.attributes_for(:update, @abstract_model).each do |name, value| @object.send("#{name}=", value) end end end end end end end end
解决方案
错误核心原因是:被排除的模型未经过RailsAdmin的参数格式化处理,原始字符串参数直接赋值给Mongoid数组字段导致类型不匹配。要让数据格式化回调优先执行,可通过以下两种方式修复:
方式一:对排除模型手动执行参数格式化
在调用assign_attributes之前,先用RailsAdmin内置参数过滤器处理参数,确保字段类型转换完成:
修改覆写代码中的UPDATE分支:
elsif request.put? # UPDATE exclude_models = ['product', 'manufacturer', 'language_code', 'vip_influencer_video_note', 'video', 'celebrity', 'vertical'] model_name = params[:model_name] # 统一处理参数格式化:原生sanitize或手动过滤 if exclude_models.include?(model_name) # 对排除模型,手动触发RailsAdmin的参数过滤逻辑 params[@abstract_model.param_key] = @abstract_model.model.param_filter.filter( params[@abstract_model.param_key], @abstract_model, request.xhr? ? :modal : :update ) else sanitize_params_for!( request.xhr? ? :modal : :update ) end # 参数已完成格式化,再执行赋值逻辑 @object.assign_attributes(params[@abstract_model.param_key]) @authorization_adapter && @authorization_adapter.attributes_for(:update, @abstract_model).each do |name, value| @object.send("#{name}=", value) end # 补充原生保存与响应逻辑(原代码缺失,会导致只赋值不保存) if @object.save flash[:success] = t('admin.flash.successful', name: @model_config.label, action: t('admin.actions.edit.done')) redirect_to_on_success else flash[:error] = t('admin.flash.error', name: @model_config.label, action: t('admin.actions.edit.done')) render :edit end end
方式二:继承原生Edit动作而非Base
避免完全重写controller逻辑,继承RailsAdmin原生Edit动作,仅修改需自定义的部分,原生参数格式化流程会自动优先执行:
module RailsAdmin module Config module Actions # 继承原生Edit动作,而非Base class Edit < RailsAdmin::Config::Actions::Edit register_instance_option :controller do proc do if request.get? # EDIT # 保留原生GET逻辑,或自定义 super() elsif request.put? # UPDATE exclude_models = ['product', 'manufacturer', 'language_code', 'vip_influencer_video_note', 'video', 'celebrity', 'vertical'] # 先执行原生参数处理与赋值逻辑 super() # 在这里添加自定义逻辑(如额外属性赋值、权限处理等) end end end end end end end
内容的提问来源于stack exchange,提问作者Suhas Shinde
相关产品推荐
相关产品推荐

