Search code examples
ruby-on-railsruby-on-rails-3activerecordrails-activerecordactivescaffold

ActiveScaffold show relational value


I have a simple table being shown in ActiveScaffold. The table has three fields: an ID, a description, and a second ID which is a foreign key to another table. In the ActiveScaffold list view, I want to show the 'name' field from the related table rather than the foreign key value (an unhelpful ID number).

I have looked over the ActiveScaffold basic documentation, FAQ, and forum (as well as doing an SO search) with no luck. The AS API docs describe a boatload of functionality associated with ActiveRecord associations, but appear to have nothing on the basics of setting up these relationships.

How can I accomplish this?


Solution

  • After two days of searching and experimentation, I ended up figuring this out. Thought I'd post it just in case this is helpful to others.

    I'll be using a Customer and Order example below (i.e. a Customer can have multiple Orders, and we want the Customer's name to show in the Order's ActiveScaffold view). The data structure is deliberately kept very simple.

    First a couple of basic definitions for the below:

    • The child table is the table that has the foreign key in it. In the example, Orders will have the foreign key from the Person table.
    • The parent table is the table being related to by the child. The Customer table is the parent in the example below.

    There are four basic steps to this process:

    1. Set up the relationship in the database.
    2. Set up the relationship in Rails.
    3. Configure ActiveScaffold so it knows what you want to do.
    4. Add a property to the child model that returns the data you want to display.

    Here's a walkthrough:

    1. Verify you’ve got a straightforward ActiveScaffold view working for the child. That process is outside the scope of this answer, but see the AS wiki for some straightforward instructions.

    2. Add a foreign key in the child table. In Rails Migration-speak, this requires the creation of the tables, with the foreign ID field in the child table, and then “add_foreign_key [child table symbol], [parent table symbol]” In my case here’s the full migration:

      class CustomerOrderAdd < ActiveRecord::Migration
        def change
          create_table(:customers, primary: :target_group_id) do |t|
            t.column :full_name, :string, null: false
          end
      
          create_table(:orders, primary: :order_id) do |t|
            t.column :customer_id, :int, null: false
            t.column :order_desc, :string, null: false
          end
      
          add_foreign_key :orders, :customers
        end
      end
      
    3. In the parent class, add has_many *child object symbol plural form* In our example, this would be:

      class Customer < ActiveRecord::Base
        has_many :orders
      end
      
    4. In the child class, add belongs_to *parent object symbol singular form* Again in our example, this leads to:

      class Order < ActiveRecord::Base
        belongs_to :target_group
        . . .
      end
      
    5. In the child controller’s ActiveScaffold config block, add a column to your ActiveScaffold columns array that will display the related data from the parent class/table. Name this whatever you’d like to name it. Keep in mind you’ll need to use this in the remaining steps. In our example, this leads to the addition of :customer_name in the columns array:

      class OrdersController < ApplicationController
        active_scaffold :order do |config|
          config.label = 'Customer Orders'
          config.list.sorting = [{customer_id: :asc}]
          config.list.per_page = 30
      
          config.columns = [:id, :order_desc, :customer_name]
      
        end
      end
      
    6. To speed things up a bit, tell ActiveScaffold to load parent table data as soon as possible. In our example, this leads to the following addition to the child controller's ActiveScaffold config area:

      config.columns[:customer_name].includes = :customer]
      
    7. Add a function in your child model named exactly the same as the new column name you added to the ActiveScaffold columns array. In this function, type out the related class/table name with a dot at the end, then the property/column name you want to actually show. In the example, this looks like:

      def customer_name
        customer.full_name
      end
      
    8. Hit your ActiveScaffold index view for the child class/table. Your value should now be in place.