使用ActiveAdmin和Friendly_id时的ActiveRecord :: ReadOnlyRecord

时间:2011-10-07 08:17:04

标签: ruby-on-rails ruby-on-rails-3 readonly activeadmin friendly-id

我最近在一个项目中开始使用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

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

http://localhost:5000/manage/spaces/the-kosmonaut/edit

如何通过尝试更新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://localhost:5000/manage/spaces/6933/edit

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

UPDATE UPDATE:删除scope_to可以解决问题。

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

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

5 个答案:

答案 0 :(得分:28)

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

我不确切知道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中创建一个前置过滤器,以便它自动继承到所有Active Admin控制器中。

首先将过滤器添加到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:清除代码以使用在过滤器添加到基本控制器之前添加Active Admin的标准方式

答案 1 :(得分:16)

您可以根据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+版本)

答案 2 :(得分:4)

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

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

答案 3 :(得分:2)

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

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

Source

答案 4 :(得分:1)

以下是基于@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