16

我最近在一个项目中开始使用 ActiveAdmin,几乎一切都很好,但是在将它与 friendly_id gem 结合使用时遇到了问题。我的表单收到 ActiveRecord::ReadOnlyRecord [我相信],因为友好 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检查控制台中的记录时?它返回假

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

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

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

4

5 回答 5

29

如果您只想要前端的友好 ID 而在 Active Admin 中不关心它们,您可以为 Active Admin 控制器恢复友好 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:我刚刚尝试将上述技巧与 friendly_id gem 一起使用,它似乎工作得很好:)

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

于 2011-10-07T08:50:48.667 回答
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+)

于 2013-08-17T14:02:56.633 回答
4

这种方法对我有用。在 app/admin/model_name.rb 中添加此代码

ActiveAdmin.register model_name do
  controller do
    defaults finder: :find_by_slug
  end
end
于 2013-12-11T15:50:09.623 回答
2

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

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

来源

于 2014-08-27T18:58:07.183 回答
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
于 2013-10-22T08:57:22.620 回答