Powered By BlogNow - Get Your Free Blog

I have moved!

My new blog is at lindsaar.net

All the old content will stay here though. But check out the new blog

¿ 21/10/2007 - Self Refferential Associations with Ruby on Rails, active_scaffold with acts_as_nested_set

Today I had to get an active_scaffold based admin interface working with a model that was a nested set.

Active Scaffold has a "config.nested.add_link" option, but this obviously didn't work just "out of the box" as a nested set does not have any Active Record associations as such.  It has children and parents, but these are not found through "has_many" and "belongs_to".

The solution was actually quite simple.  Use the Active Scaffold reverse association method and define the association.

So i thought I would write it up here.

First, you have a page model.  A page (like a web page) belongs in a tree.  A page has a parent and has many children.  I am using this as an interface to a website, the root is the home page, which has many links, all of which are children to the root page.  Those children can also have children which are then sub pages to that subject.

So we have a page migration and model that looks basically like this:

Migration:

class CreatePages < ActiveRecord::Migration
  def self.up
    create_table :pages do |t|
      t.column :title, :string
      t.column :parent_id, :integer
      t.column :rgt, :integer
      t.column :lft, :integer
    end
  end

  def self.down
    drop_table :pages
  end
end

Model:

class Page < ActiveRecord::Base
  acts_as_nested_set
end


To add the active scaffold work to this, you have to define a self-refferential association between a Page and it's children.  As the child is also a page, you use the foreign_key and class_name options to the belongs to and has many.

So this model code becomes:

class Page < ActiveRecord::Base
  acts_as_nested_set
 
  belongs_to :parent,
             :foreign_key => :parent_id,
             :class_name => "Page"
  has_many   :children,
             :foreign_key => :parent_id,
             :class_name => "Page"
end

As we already have a "parent_id" as part of the nested set, we can conveniently use this for the association foreign key.

Then, in the controller we can do:

class PagesController < ApplicationController

  active_scaffold :page do |config|
    config.columns[:children].association.reverse = :parent
    config.nested.add_link("Show Children", [:children])
  end

end

end

The first line in the config tells Active Scaffold explicitly what the reverse association is.  This is needed as the self referrential nested set doesn't just work straight away as it is a bit of a unique case.

The second line simply provides a link on each record to allow you to show it's children.

I hope that helps someone else.  Not hard, but will save you the 15 minutes putting the code together.

blogLater


Mikel
Post A Comment! :: Send to a Friend!

¿ 6/5/2008 - Thanks Much for a great post

Posted by Anonymous
This saved me a ton of time
Permanent Link

About Me

AKA Raasdnil, this site is about web coding, hosting and all other matters that relate to this... especially Ruby on Rails!

Links