Search code examples
ruby-on-rails-5activeadmin

How to select a model field to represent a many to one relationship on active admin dashboard?


I am building an eCommerce website using rails 5 and activeadmin gem to manage my dashboard. I have a product and a category model in a many to one relationship.

class Product < ApplicationRecord
    before_destroy :not_referenced_by_any_line_item
    belongs_to :category
    has_many :line_items, dependent: :destroy
    has_many :reviews, dependent: :destroy

    def self.search(search)
        all.where("lower(title) LIKE :search", search: "%#{search}%")
    end 

    private

    def not_referenced_by_any_line_item
        unless line_items.empty?
            errors.add(:base, "line items present")
            throw :abort
        end
    end
end
class Category < ApplicationRecord
    has_many :products, dependent: :destroy

    def self.search(search)
        all.where("lower(category_name) LIKE :search", search: "%#{search}%")
    end 
end

I then registered the models to the activeadmin dashboard as below

ActiveAdmin.register Product do

  permit_params :title, :description, :availability, 
  :price, :photo_link, :category_id, :advert, :pictureOne, 
  :pictureTwo, :pictureThree

end

ActiveAdmin.register Category do

  permit_params :category_name, :photos

end

I can now select a product category on the project form when creating a product but the problem is, instead of a category name or any other field to display on the project category form input field so that you know exactly which category you are selecting,an abject is being displayed making it difficult to know which category you are selecting. display of dropdown of product category input form field: enter image description here


Solution

  • ActiveAdmin's default functionality is to look for a name field on a given model when deciding what to render as the record's identifier. If the model doesn't have a name field, ActiveAdmin doesn't how else to let you which record you're dealing with besides being able to show you a stringified mess of the location where that record is in memory (It's the same string you'd get if you did Category.first.to_s in the console).

    To get the ActiveAdmin to recognize the name, you have to override the default edit form it creates for you so you can customize the select label.

    You'll add all the fields you want to be editable to the form. When you get adding the input for the category, you specify that you want that field to be a select and you can customize the select's label, like so:

    # app/admin/product.rb
    
    ActiveAdmin.register Product do
      permit_params :title, :description, :availability, 
      :price, :photo_link, :category_id, :advert, :pictureOne, 
      :pictureTwo, :pictureThree
    
      form do |f|
        f.inputs do
          # Add a form input for the category
          #
          # This approach also allows you to specify which categories you 
          # allow to be selected, in the "collection" attribute.
          #
          # Inside the "map" call, you have the proc return an array with the first item 
          # in the array being the name of the category (the label for the select) 
          # and the second item being the category's ID (the select's value)
          f.input :category_id, label: 'Category', as: :select, collection: Category.all.map{ |c| [c.category_name, c.id]}
    
          # Then add other inputs
          f.input :title
          f.input :description
          f.input :availability
    
          # ...
          # (Add f.input for the rest of your fields)
        end
    
        f.actions
      end
    end
    

    You'll follow similar methods when you need to render the name a category in other places in ActiveAdmin.

    If it's not too much trouble, you'll probably be better off renaming category_name on your Category model to just name. That way, you'll be fighting with ActiveAdmin a lot less and won't need to make customizations like this as much.