administrate: Namespaced models not working

Per #871 and #179, it looks like namespaced models were worked on before, but I’m still getting the same issue. Going to the “New <model>” page or trying to edit an object that has PaperTrail results in:

uninitialized constant PaperTrail::VersionDashboard

Which looks to be the same namespacing issue as before. I’m on version 0.11.0 of the gem, as well as Rails 5.2.1 and Ruby 2.5.1.

About this issue

  • Original URL
  • State: open
  • Created 5 years ago
  • Reactions: 2
  • Comments: 20 (3 by maintainers)

Commits related to this issue

Most upvoted comments

Adding a bit more of context.

The main issue is that Administrate assumes the names of models following Rails conventions only. There isn’t an official way of telling a dashboard that it should be serving a model different from the one named in the current URL.

Due to this, if you want to provide a dashboard for a model, you must ensure that all necessary pieces are in place, and have the expected names.

For example, for PaperTrail::Version you will need the following.

The admin controller will be under the Admin namespace (or whatever you call it in your app), as well as the PaperTrail namespace because it’s part of the model name:

### app/controllers/admin/paper_trail/versions_controller.rb
module Admin
  module PaperTrail
    class VersionsController < Admin::ApplicationController
    end
  end
end

The route will reflect this namespacing too:

### config/routes.rb
Rails.application.routes.draw do
  namespace :admin do
    resources :products
    namespace :paper_trail do
      resources :versions
    end
  end
end

The dashboard will also need the PaperTrail namespace:

### app/dashboards/paper_trail/version_dashboard.rb
require "administrate/base_dashboard"

module PaperTrail
  class VersionDashboard < Administrate::BaseDashboard
    ATTRIBUTE_TYPES = {
      event: Field::String,
      object: Field::Text,
    }.freeze

    COLLECTION_ATTRIBUTES = ATTRIBUTE_TYPES.keys
    SHOW_PAGE_ATTRIBUTES = ATTRIBUTE_TYPES.keys
    FORM_ATTRIBUTES = ATTRIBUTE_TYPES.keys
  end
end

With any other dashboards referring to your model via associations, use the option class_name to specify the model class:

### app/dashboards/product_dashboard.rb
ATTRIBUTE_TYPES = {
  id: Field::Number,
  name: Field::String,
  code: Field::String,
  price_in_cents: Field::Number,
  stock: Field::Number,
  versions: Field::HasMany.with_options(class_name: 'PaperTrail::Version'),
  created_at: Field::DateTime,
  updated_at: Field::DateTime,
}.freeze

And that should be it.

The situation is not ideal, and it stems from the way that ResourceResolver works, which I think is a bit too clever. It’s related to https://github.com/thoughtbot/administrate/issues/405, where I propose a potential solution (which would need a PR). Still, this doesn’t mean yet that such a solution is going to happen any time soon. It requires a bit of thought, and for this we require a bit of time. Personally I’d rather prune the long list of issues a bit before introducing big changes. That will help us understand better what sort of use cases people are getting into.

Or, someone could try implement the solution at #405, and at least we’ll have real code to discuss. Doesn’t mean it will definitely be merged! But at least it helps understanding the problem better.

Looks like I reached a workable state in a project combining Globalize v5.3.0, PaperTrail v10.3.1 (which both use namespaced models) with Administrate v0.12.0, using the following steps:

  1. Remove all previous admin-related config (I had a mix of custom admin pages and RailsAdmin). In particular in routes.rb, removed my old namespace :admin do root ‘pages#home’ ... otherwise got error Page not found when running generator.
  2. Run the generator: rails generate administrate:install. NB: it worked but gave WARNING: Unable to generate a dashboard for PaperTrail::Version. Administrate does not yet support namespaced models. WARNING: Unable to generate a dashboard for Thought::Translation. Administrate does not yet support namespaced models, etc (1 warning about PaperTrail and several others about Globalized models).
  3. For each of these warnings:
    • run something like rails generate administrate:dashboard PaperTrail::Version, rails generate administrate:dashboard Thought::Translation, etc
    • A dashboard configuration file like dashboards/translations_dashboard.rb gets generated successfully with a class such as Thought::TranslationDashboard and various configurations inside, but it is in the wrong directory dashboards --> move it to a new sub-directory which matches the namespace: dashboards/thoughts/translations_dashboard.rb, etc
    • similarly, an admin controller gets generated at the wrong level, move it like this: controllers/translations_controllers.rb --> controllers/thought/translations_controllers.rb
    • Add to routes.rb inside the newly generated admin namespace: namespace :paper_trail do resources :versions end, namespace :post do resources :translations end, etc
  4. At this point the app can be started, and all these routes work without giving an error page: http://localhost:3000/admin, http://localhost:3000/admin/thoughts, http://localhost:3000/admin/thoughts/25, http://localhost:3000/admin/thoughts/25/edit, http://localhost:3000/admin/thought/translations, http://localhost:3000/admin/thought/translations/27.
  5. When viewing http://localhost:3000/admin/thought/translations/27/edit (editing a thought translation) there was the error undefined method 'globalized_model_id', this is because Thought::TranslationDashboard was generated with FORM_ATTRIBUTES = %i[ globalized_model thought_id locale quote ]. I removed globalized_model from this array and then the translation edit page could be loaded correctly.

Some other configurations were then also needed (e.g for models using ActiveStorage attachments, or models with custom to_param method, etc), but these were not related to this namespaced models issue.

In case anyone else is experiencing issues with getting the namespaced models to work both on development and in production, here’s what works for me on Rails 5.2.2 Let’s say you have a namespaced model called Foo::Bar

Your controller should look like this:

# app/controllers/admin/foo/bars_controller.rb

module Admin::Foo
  class BarsController < Admin::ApplicationController
  end
end

If you are using administrate in namespaced mode (eg. if it’s accessible via /manager instead of /admin), then just replace Admin with Manager and move the controller to app/controllers/manager/foo/bars_controller.rb

**NOTE**: If you define your controller as below, it won't work on production, at least not on rails 5.2.2
# app/controllers/admin/foo/bars_controller.rb

module Admin
  class Foo::BarsController < Admin::ApplicationController
  end
end

Your dashboard should look like this:

# app/dashboards/foo/bar_dashboard.rb

require 'administrate/base_dashboard'

class Foo::BarDashboard < Administrate::BaseDashboard
  ATTRIBUTE_TYPES = {...}.freeze

  COLLECTION_ATTRIBUTES = [...].freeze

  SHOW_PAGE_ATTRIBUTES = [...].freeze

  FORM_ATTRIBUTES = [...].freeze
end

Your routes should look like this

# config/routes.rb
namespace :admin do
    namespace :foo do
      resources :bars
    end
end

I had the same issue as in the OP and could fix it in my case with same code as suggested (and didn’t encounter any refresh issues so far):

# config/routes.rb
  namespace :admin do
    ...
    namespace :paper_trail do
      resources :versions
    end
  end

# app/controllers/admin/paper_trail/versions_controller.rb
module Admin
  class PaperTrail::VersionsController < Admin::ApplicationController
    # To customize the behavior of this controller,
    # you can overwrite any of the RESTful actions. For example:
    #
    # def index
    #   super
    #   @resources = PaperTrail::Version.
    #     page(params[:page]).
    #     per(10)
    # end

    # Define a custom finder by overriding the `find_resource` method:
    # def find_resource(param)
    #   PaperTrail::Version.find_by!(slug: param)
    # end

    # See https://administrate-prototype.herokuapp.com/customizing_controller_actions
    # for more information
  end
end

# app/dashboards/paper_trail/version_dashboard.rb
require "administrate/base_dashboard"

class PaperTrail::VersionDashboard < Administrate::BaseDashboard
  # ATTRIBUTE_TYPES
  # a hash that describes the type of each of the model's fields.
  #
  # Each different type represents an Administrate::Field object,
  # which determines how the attribute is displayed
  # on pages throughout the dashboard.
  ATTRIBUTE_TYPES = {
    item: Field::Polymorphic,
    id: Field::Number,
    event: Field::String,
    whodunnit: Field::String,
    object: Field::Text,
    created_at: Field::DateTime,
  }.freeze

  # COLLECTION_ATTRIBUTES
  # an array of attributes that will be displayed on the model's index page.
  #
  # By default, it's limited to four items to reduce clutter on index pages.
  # Feel free to add, remove, or rearrange items.
  COLLECTION_ATTRIBUTES = [
    :item,
    :id,
    :event,
    :whodunnit,
  ].freeze

  # SHOW_PAGE_ATTRIBUTES
  # an array of attributes that will be displayed on the model's show page.
  SHOW_PAGE_ATTRIBUTES = [
    :item,
    :id,
    :event,
    :whodunnit,
    :object,
    :created_at,
  ].freeze

  # FORM_ATTRIBUTES
  # an array of attributes that will be displayed
  # on the model's form (`new` and `edit`) pages.
  FORM_ATTRIBUTES = [
    :item,
    :event,
    :whodunnit,
    :object,
  ].freeze

  # Overwrite this method to customize how versions are displayed
  # across all pages of the admin dashboard.
  #
  # def display_resource(version)
  #   "PaperTrail::Version ##{version.id}"
  # end
end

Should Administrate generate this automatically?

Regarding the problem with Manyhands::Tenant, it may have been because of the plural namespace. This has been fixed now with https://github.com/thoughtbot/administrate/pull/2261

When setting up for ActiveStorage the instructions from Pablo above worked well but I had to scope the routes as follows:

namespace :admin do
  scope module: :active_storage do
    resources :attachments
    resources :blobs
  end
end

I have problem when namespaces are plurals. In my case shops

This is my very bad fix inside the gem

immagine