使用ActiveAdmin和Friendly_id时的ActiveRecord :: ReadOnlyRecord

我最近在一个项目中开始使用ActiveAdmin,几乎所有东西都运行良好,但是当我将它与friendly_id gem结合使用时遇到了问题。 我正在为我的表单[我相信]抛出ActiveRecord :: ReadOnlyRecord,因为friendly_id属性的ID是只读的:

{"utf8"=>"✓", "_method"=>"put", "authenticity_token"=>"Rc5PmUYZt3BiLvfPQr8iCPPXlbfgjoe/n+NhCwXazNs=", "space"=>{"name"=>"The Kosmonaut", "address"=>"8 Sichovykh Striltsiv 24", "email"=>"info@somedomain.com"}, "commit"=>"Update Space", "id"=>"the-kosmonaut"} <--- culprit 

我猜最后一行是罪魁祸首,因为它是一个只读属性,它不是我的forms,而是在PATH中

HTTP://本地主机:5000 /管理/空间/的-kosmonaut /编辑

如何通过尝试更新ID来解决此问题?

在ActiveAdmin中的表单如下所示:

  form do |f| f.inputs "Details" do f.input :name f.input :address f.input :email f.input :phone f.input :website end f.inputs "Content" do f.input :description f.input :blurb end f.buttons end 

更新:这不起作用,所以它不是friendly_id?

我尝试使用@ watson的建议应该有效,但仍然有同样的错误;-(

 {"utf8"=>"✓", "_method"=>"put", "authenticity_token"=>"Rc5PmUYZt3BiLvfPQr8iCPPXlbfgjoe/n+NhCwXazNs=", "space"=>{"name"=>"The Kosmonaut 23"}, "commit"=>"Update Space", "id"=>"6933"} 

HTTP://本地主机:5000 /管理/空间/ 6933 /编辑

当我用record.readonly检查控制台中的记录时? 它返回false

更新更新:删除scope_to可以解决问题。

 scope_to :current_user, :unless => proc{ current_user.admin? } 

唯一的问题是我需要使用scope_to来防止用户看到他们不拥有的记录。 我的猜测是(因为我假设scope_to通常与has_many一起使用)我的HABTM关联会导致一些奇怪吗? 即用户空格?

如果您只想在前端使用友好ID而不关心Active Admin中的内容,则可以为您的Active Admin控制器恢复friendly_id gem的效果。

to_param知道friendly_id如何覆盖to_param方法,但如果它以正常方式执行,则在所有Active Admin控制器中重新覆盖它应该修复它,例如:

 ActiveAdmin.register Foobar do before_filter do Foobar.class_eval do def to_param id.to_s end end end end 

更好的是,您可以在基本Active Admin控制器ActiveAdmin::ResourceController创建一个beforefilter,以便它自动inheritance到所有Active Admin控制器中。

首先将filter添加到config/initializers/active_admin.rb设置:

 ActiveAdmin.setup do |config| # ... config.before_filter :revert_friendly_id end 

打开ActiveAdmin::ResourceController并添加一个revert_friendly_id方法,例如,在config/initializers/active_admin.rb的末尾添加以下内容:

 ActiveAdmin::ResourceController.class_eval do protected def revert_friendly_id model_name = self.class.name.match(/::(.*)Controller$/)[1].singularize # Will throw a NameError if the class does not exist Module.const_get model_name eval(model_name).class_eval do def to_param id.to_s end end rescue NameError end end 

更新:我刚刚更新了最后一个代码示例来处理没有相关模型的控制器(例如Active Admin Dashboard控制器)

更新2:我刚刚尝试将上述hack与friendly_id gem一起使用,它似乎工作得很好:)

更新3:清除代码以使用在filter之前将Active Admin添加到基本控制器的标准方法

您可以根据http://activeadmin.info/docs/2-resource-customization.html#customizing_resource_retrieval自定义资源检索。 请注意,您要使用find_resource方法而不是resource ,否则您将无法创建新记录。

(查看https://github.com/gregbell/active_admin/blob/master/lib/active_admin/resource_controller/data_access.rb了解更多详情)

在您的ActiveAdmin资源类中写入:

 controller do def find_resource scoped_collection.where(slug: params[:id]).first! end end 

您还可以通过在active_admin.rb初始化程序中修改ResourceController来覆盖所有资源的行为。

 ActiveAdmin::ResourceController.class_eval do def find_resource if scoped_collection.is_a? FriendlyId scoped_collection.where(slug: params[:id]).first! else scoped_collection.where(id: params[:id]).first! end end end 

希望有所帮助!

附注:通过管理界面创建新记录时,请确保不在表单中包含slug字段,否则FriendlyId将不会生成slug。 (我相信只有5+版本)

这种方法适合我。 在app / admin / model_name.rb中添加此代码

 ActiveAdmin.register model_name do controller do defaults finder: :find_by_slug end end 

要添加Denny的解决方案 ,更友好的解决方案是使用friendly_id的查找器。

 controller do def find_resource scoped_collection.friendly.find_by_friendly_id(params[:id]) end end 

资源

这是我基于@Thomas解决方案的解决方案

 ActiveAdmin.setup do |config| # ... config.before_filter :revert_friendly_id, :if => -> { !devise_controller? && resource_controller? } end # override #to_param method defined in model in order to make AA generate # routes like /admin/page/:id/edit ActiveAdmin::BaseController.class_eval do protected def resource_controller? self.class.superclass.name == "ActiveAdmin::ResourceController" end def revert_friendly_id model_name = self.class.name.match(/::(.*)Controller$/)[1].singularize # Will throw a NameError if the class does not exist Module.const_get model_name eval(model_name).class_eval do def to_param id.to_s end end rescue NameError end end