I started using ActiveAdmin recently in a project and almost everything works great but I'm having a problem when using it in combination with the friendly_id gem. I'm getting ActiveRecord::ReadOnlyRecord thrown for my forms [i believe] because of the friendly_id attribute whose ID is readonly:
{"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
I'm guessing the last line is 开发者_开发技巧the culprit as it's a readonly attribute, it's not in my form but rather in the PATH
http://localhost:5000/manage/spaces/the-kosmonaut/edit
How can I fix this from trying to update the ID?
Form from in ActiveAdmin looks like this:
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: This doesn't work either so it's not the friendly_id?
I tried using @watson's suggestion which should have worked but still got the same error ;-(
{"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
When I check the record in the console with record.readonly? it returns false
UPDATE UPDATE: removing the scope_to fixes the problem.
scope_to :current_user, :unless => proc{ current_user.admin? }
Only problem is I need the scope_to to prevent users from seeing records they do not own. My guess is (as I'm assuming scope_to normally works with has_many) that my HABTM association causes some weirdness? Ie Users <-- HABTM --> Spaces?
If you only want friendly ID's in the front end and don't care about them inside Active Admin, you can revert the effects of the friendly_id gem for your Active Admin controllers.
I don't know exactly how friendly_id overrides the to_param
method, but if it's doing it the normal way, re-overriding it inside all of your Active Admin controllers should fix it, e.g.:
ActiveAdmin.register Foobar do
before_filter do
Foobar.class_eval do
def to_param
id.to_s
end
end
end
end
Even better you could create a before filter in the base Active Admin controller ActiveAdmin::ResourceController
so that it is automatically inherited into all your Active Admin controllers.
First add the filter to the config/initializers/active_admin.rb
setup:
ActiveAdmin.setup do |config|
# ...
config.before_filter :revert_friendly_id
end
The open up ActiveAdmin::ResourceController
and add a revert_friendly_id
method, E.g. by adding the following to the end of 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
Update: I just updated the last code example to handle controllers with no related model (e.g. the Active Admin Dashboard controller)
Update 2: I just tried using the above hack together with the friendly_id gem and it seems to work just fine :)
Update 3: Cleaned up the code to use the standard way of adding Active Admin before filters to the base controller
You can customize the resource retrieval according to http://activeadmin.info/docs/2-resource-customization.html#customizing_resource_retrieval. Note that you want to use the find_resource
method instead of resource
, or you won't be able to create new records.
(Check https://github.com/gregbell/active_admin/blob/master/lib/active_admin/resource_controller/data_access.rb for more details)
In your ActiveAdmin resource class write:
controller do
def find_resource
scoped_collection.where(slug: params[:id]).first!
end
end
You can also overwrite the behavior for all resources by modyfing the ResourceController in the active_admin.rb
initializer.
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
Hope that helps!
Side note: When creating new records through the admin interface make sure you don't include the slug field in the form, or FriendlyId will not generate the slugs. (I believe that's for version 5+ only)
This method works for me. add this code in app/admin/model_name.rb
ActiveAdmin.register model_name do
controller do
defaults finder: :find_by_slug
end
end
To add to Denny's solution, a more "friendly" solution would be to use friendly_id's finders.
controller do
def find_resource
scoped_collection.friendly.find_by_friendly_id(params[:id])
end
end
Source
Here is my solution based on @Thomas solution
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
精彩评论