16

我最近在一個項目中開始使用ActiveAdmin,幾乎所有的工作都很好,但是在與friendly_id gem結合使用時遇到問題。我得到的ActiveRecord :: ReadOnlyRecord拋出我的,因爲friendly_id屬性,其ID的形式[我相信]是隻讀的:使用ActiveAdmin和Friendly_id時ActiveRecord :: ReadOnlyRecord

{"utf8"=>"âœ「", 
"_method"=>"put", 
"authenticity_token"=>"Rc5PmUYZt3BiLvfPQr8iCPPXlbfgjoe/n+NhCwXazNs=", 
"space"=>{"name"=>"The Kosmonaut", 
"address"=>"8 Sichovykh Striltsiv 24", 
"email"=>"[email protected]"}, 
"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 

UPDATE:這不工作,要麼所以它不是friendly_id?

我試着使用@華生的建議,這應該有工作,但仍然得到了同樣的錯誤;-(

{"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 - >空間?

回答

28

如果您只希望在前端使用友好的ID,並且不在意Active Admin內的這些ID,則可以恢復Active Admin控制器的friendly_id gem效果。

我不知道覆蓋究竟如何friendly_id的to_param方法,但如果它這樣做的正常方式,重新覆蓋它裏面所有的主動管理控制器應該修復它,例如:

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 

更新:我剛剛更新的最後的代碼示例來處理與沒有相關的模型控制器(例如主動管理儀表板控制器)

更新2:我只是在一起使用上述黑客與friendly_id寶石嘗試,似乎就好:)

更新3的工作: - 清理代碼使用在過濾器之前將Active Admin添加到基本控制器的標準方式

+1

我可能是錯誤的,我的假設,你的解決方案應該有工作,但我還是不能修改記錄。我改變了這個問題來反映這一點。 – holden

+0

Friendly_id似乎是一個紅鯡魚。如果沒有我的current_user scope_to,它可以正常使用friendly_id ... ;-( – holden

+0

我可以使用friendly_id和我的上述方法修改記錄,並且已經用我認爲與friendly_id無關的內容更新了問題問題,但與授權有關如果您需要授權,我建議您看看CanCan –

14

您可以根據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初始化modyfing的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+)

+1

這是正確的答案 – jeffsaracco

+0

我不認爲這將工作,如果在我的模型中使用「範圍」slug。即。slu are只有在與其他財產/協會結合使用時才能保證獨一無二。在這種情況下的任何提示? – elsurudo

+0

非常感謝丹尼,問題解決者! – Gibson

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 
+0

這對我有用,當我有一個registered_pa​​ge。例如:ActiveAdmin.register_page「儀表板」謝謝@Timur你救了我的一天。 – RoundOutTooSoon

+0

我建議您避免使用這種技術,它會在多線程環境中修改具有不一致結果的模型類。 –

3

此方法適用於我,我的解決方案。在應用程序中添加此代碼/管理/ model_name.rb

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

要添加到Denny's solution,更「友好」的解決方案是使用friendly_id的發現者。

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

Source

相關問題