Reputation: 2837
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.
Upvotes: 2
Views: 427
Reputation: 6324
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.
Upvotes: 1
Reputation: 75025
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.
Upvotes: 5