views:

844

answers:

3

I understand how STI works, in that I have say a Post model that contains posts on a forum and several sub-posts like 'ordinaryUserPost' and 'adminUserPost' etc.

Now, I want to define the same method in each of the sub-posts, but the method would do something different in each case, eg

class Post < ActiveRecord::Base
end

class AdminUserPost < Post
  def background_color
    'rockstar red'
  end
end

class OrdinaryUserPost < Post
  def background_color
    'pale blue'
  end
end

(yes its a silly example). Now in my thread controller I do Post.find (:all) and it gives me a list of posts I need to render, but they are 'Post' objects, not AdminUserPost or OrdinaryUserPost - so I cannot just get my background_color method! I would have to do a find on each type of user post separately ...

Is there anyway I can do:

Post.find(:all)

And in the resultant array get a list of AdminUserPost and OrdinaryUserPost objects instead of Post objects?

Or is there a nice way of 'casting' my Post objects into AdminUserPost's and OrdinaryUserPost's as appropriate?

EDIT:

This works as expected - provided you have a column called 'type' in the Post class. If your column is called something else, such as 'post_type' then you need to add:

self.inheritance_column = 'post_type'

In ALL the child models (AdminUserPost and OrdinaryUserPost in this example) and in the parent (Post).

Thanks,

Stephen.

A: 

Post.find(:all) will give you Post, becouse you are serching in Post, not in AdminUserPost. If you want AdminUserPost use:

AdminUserPost.find(:all)

but I guess it will find the same posts as Post.find(:all) will find.

I think named_scope will help you:

# in model
Class Post << ActiveRecord::Base
  named_scope :admin_posts, :conditions => {:owner == 'admin'}
  named_scope :ordinary_user_posts, :condition => {:owner != 'admin'}
end

# in controller
@posts = Post.admin_posts # returns admin posts

# or
@posts = AdminUserPost.admin_posts # returns admin posts in AdminUserPost class

I didn't try to use this inherited classes, but I think it will work.

You can learn more about named scopes here: http://railscasts.com/episodes/108-named-scope

EDIT:

I'm sorry, I'm new in Rails community and I didn't hear about STI before. dbarker is right, I tried it and it works as you wanted.

klew
Named scope isn't what I am looking for as that would result in two queries. I was hoping for a way to get all posts in one go, and for Rails to auto translate them into the correct child type!
Stephen ODonnell
If STI is set up correctly, Post.find(:all) will get all subclasses.
Sarah Mei
+7  A: 

Double check that the posts table has a column 'type' (string). If the AdminUserPosts and OrdinaryUserPosts are written to the table 'posts' and the type column is correct, you should get the subclass behavior you expect.

dbarker
I am an idiot! My 'type' column was actually called 'post_type', so I had added self.inheritance_column = 'post_type' in the CHILD models, but forgot to add it in the parent - adding it in the parent got things working immediately - thanks!
Stephen ODonnell
+1  A: 

This works as expected - provided you have a column called 'type' in the Post class. If your column is called something else, such as 'post_type' then you need to add:

self.inheritance_column = 'post_type'

In ALL the child models (AdminUserPost and OrdinaryUserPost in this example) and in the parent (Post).

Stephen ODonnell