使用ActiveAdmin和Friendly_id时的ActiveRecord :: ReadOnlyRecord

hol*_*den 16 ruby-on-rails readonly friendly-id ruby-on-rails-3 activeadmin

我最近在一个项目中开始使用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
Run Code Online (Sandbox Code Playgroud)

我猜最后一行是罪魁祸首,因为它是一个只读属性,它不是我的形式,而是在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
Run Code Online (Sandbox Code Playgroud)

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

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

{"utf8"=>"✓",
 "_method"=>"put",
 "authenticity_token"=>"Rc5PmUYZt3BiLvfPQr8iCPPXlbfgjoe/n+NhCwXazNs=",
 "space"=>{"name"=>"The Kosmonaut 23"},
 "commit"=>"Update Space",
 "id"=>"6933"}
Run Code Online (Sandbox Code Playgroud)

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

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

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

scope_to :current_user, :unless => proc{ current_user.admin? }
Run Code Online (Sandbox Code Playgroud)

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

Tho*_*son 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
Run Code Online (Sandbox Code Playgroud)

更好的是,您可以在基本Active Admin控制器中创建一个前置过滤器,ActiveAdmin::ResourceController以便它自动继承到所有Active Admin控制器中.

首先将过滤器添加到config/initializers/active_admin.rb设置中:

ActiveAdmin.setup do |config|
  # ...
  config.before_filter :revert_friendly_id
end
Run Code Online (Sandbox Code Playgroud)

打开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
Run Code Online (Sandbox Code Playgroud)

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

更新2:我刚尝试使用上面的hack和friendly_id gem,它似乎工作得很好:)

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


Den*_*nny 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
Run Code Online (Sandbox Code Playgroud)

您还可以通过在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
Run Code Online (Sandbox Code Playgroud)

希望有所帮助!

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