views:

108

answers:

2

Hi,

I've got these tables:

**Sites**
:has_many :blogs
:has_many :pages

**Blogs**
:belongs_to :site

**Pages**
:belongs_to :site
:belongs_to :blog

Basically, I want to be able to create Pages that are either related to a Site OR related to a Blog with routes like this:

/blogs/1/pages/1
/sites/1/pages/2

With my current setup, my Pages table has a foreign_key for blog_id AND site_id - and I was just thinking of doing this:

  • if a page is being created for a site (meaning it doesn't belong to a blog) then set blog_id = to NULL, but set site_id accordingly

  • but, if a page is being created for a blog (which already belongs to a site) then set the related site_id AND blog_id

Then if I want a list of Site pages: I can just query the Pages table for all NULL blog_ids, and if I want Blog pages, I'll get them through the relationship with Blog already.

UPDATE: I accepted the answer below which suggested using "polymorphic associations", but could this also be done using Single Table Inheritance? If so, which method is better?

Thanks.

+5  A: 

You can use polymorphic associations.

Add a foreign key and a type column to your pages table. Find an adjective that describes the common property of the classes that your pages can belong to. I came up with pageable (which gives me a pageable_id and pageable_type column). If you use migrations, add the following your Page migration:

# Adds "pageable_id" integer column and "pageable_type" string column.
t.references(:pageable, :polymorphic => true)

In your models, specify the polymorphic relationship when using has_many/belongs_to:

class Site < ActiveRecord::Base
  has_many :pages, :as => :pageable
end

class Blog < ActiveRecord::Base
  has_many :pages, :as => :pageable
end

class Page < ActiveRecord::Base
  belongs_to :pageable, :polymorphic => true
end

And behold:

# Return all pages belonging to Site with ID 12, that is, return all pages
# with pageable_id 12 and pageable_type "site".
Site.find(12).pages

# Return all pages belonging to Blog with ID 3, that is, return all pages
# with pageable_id 3 and pageable_type "blog".
Blog.find(3).pages

# Returns the owner (Blog or Site) of Page with ID 27.
Page.find(27).pageable

I hope this helps.

molf
+1  A: 

You should look into Polymorphic assocations: http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html

Basically, you're on the right track, but there is an existing pattern for what you're trying to do.

Matt Darby