ActiveRecord::ReadOnlyRecord when using ActiveAdmin and Friendly_id

前端 未结 5 1934
梦如初夏
梦如初夏 2020-12-08 16:36

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 ge

相关标签:
5条回答
  • 2020-12-08 17:18

    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)

    0 讨论(0)
  • 2020-12-08 17:23

    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
    
    0 讨论(0)
  • 2020-12-08 17:24

    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

    0 讨论(0)
  • 2020-12-08 17:24

    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
    
    0 讨论(0)
  • 2020-12-08 17:33

    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

    0 讨论(0)
提交回复
热议问题