Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails Associations - has_many => :through - but same model

What I am trying to do:

I have a blog and want to show related posts below the main post.

class Post < ActiveRecord::Base

  has_many :related_posts
  has_many :posts, :through => :related_posts

end

And then in the join model/table

class RelatedPost < ActiveRecord::Base

  belongs_to :post

end

And of course there is a table called related_posts with two post_id columns.

Obviously there are several flaws with this, I'm just not sure how to make this association work in Rails.

like image 474
thenengah Avatar asked Jul 02 '11 19:07

thenengah


People also ask

How would you choose between Belongs_to and Has_one?

They essentially do the same thing, the only difference is what side of the relationship you are on. If a User has a Profile , then in the User class you'd have has_one :profile and in the Profile class you'd have belongs_to :user . To determine who "has" the other object, look at where the foreign key is.

What is polymorphic association in Rails?

In Ruby on Rails, a polymorphic association is an Active Record association that can connect a model to multiple other models. For example, we can use a single association to connect the Review model with the Event and Restaurant models, allowing us to connect a review with either an event or a restaurant.


2 Answers

That was an interesting question.

I just created a working app for your use case.

post.related_posts will give you all posts related from post, while post.inverse_related_posts will give you all posts related to post.

Here's what my models look like:

class Post < ActiveRecord::Base
  has_many :related_posts_association, :class_name => "RelatedPost"
  has_many :related_posts, :through => :related_posts_association, :source => :related_post
  has_many :inverse_related_posts_association, :class_name => "RelatedPost", :foreign_key => "related_post_id"
  has_many :inverse_related_posts, :through => :inverse_related_posts_association, :source => :post
end

class RelatedPost < ActiveRecord::Base
  belongs_to :post
  belongs_to :related_post, :class_name => "Post"
end

My schema:

ActiveRecord::Schema.define(:version => 20110702194300) do

  create_table "posts", :force => true do |t|
    t.string   "name"
    t.datetime "created_at"
    t.datetime "updated_at"
  end

  create_table "related_posts", :force => true do |t|
    t.integer  "post_id"
    t.integer  "related_post_id"
    t.datetime "created_at"
    t.datetime "updated_at"
  end

end

Here's a dump of a console session that demonstrating the relationship.

ruby-1.9.2-p180:001:0>> p = Post.create! name: "Hello"
  SQL (23.5ms)  INSERT INTO "posts" ("created_at", "name", "updated_at") VALUES (?, ?, ?)  [["created_at", Sat, 02 Jul 2011 20:03:43 UTC +00:00], ["name", "Hello"], ["updated_at", Sat, 02 Jul 2011 20:03:43 UTC +00:00]]
# => #<Post id: 1, name: "Hello", created_at: "2011-07-02 20:03:43", updated_at: "2011-07-02 20:03:43">
ruby-1.9.2-p180:002:0>> p2 = Post.create! name: "World"
  SQL (1.0ms)  INSERT INTO "posts" ("created_at", "name", "updated_at") VALUES (?, ?, ?)  [["created_at", Sat, 02 Jul 2011 20:03:48 UTC +00:00], ["name", "World"], ["updated_at", Sat, 02 Jul 2011 20:03:48 UTC +00:00]]
# => #<Post id: 2, name: "World", created_at: "2011-07-02 20:03:48", updated_at: "2011-07-02 20:03:48">
ruby-1.9.2-p180:003:0>> p.related_posts
  Post Load (0.2ms)  SELECT "posts".* FROM "posts" INNER JOIN "related_posts" ON "posts"."id" = "related_posts"."related_post_id" WHERE "related_posts"."post_id" = 1
# => []
ruby-1.9.2-p180:004:0>> p2.related_posts
  Post Load (0.4ms)  SELECT "posts".* FROM "posts" INNER JOIN "related_posts" ON "posts"."id" = "related_posts"."related_post_id" WHERE "related_posts"."post_id" = 2
# => []
ruby-1.9.2-p180:005:0>> p.related_posts << p2
  SQL (0.7ms)  INSERT INTO "related_posts" ("created_at", "post_id", "related_post_id", "updated_at") VALUES (?, ?, ?, ?)  [["created_at", Sat, 02 Jul 2011 20:04:01 UTC +00:00], ["post_id", 1], ["related_post_id", 2], ["updated_at", Sat, 02 Jul 2011 20:04:01 UTC +00:00]]
# => [#<Post id: 2, name: "World", created_at: "2011-07-02 20:03:48", updated_at: "2011-07-02 20:03:48">]
ruby-1.9.2-p180:006:0>> RelatedPost.all
  RelatedPost Load (0.4ms)  SELECT "related_posts".* FROM "related_posts" 
# => [#<RelatedPost id: 1, post_id: 1, related_post_id: 2, created_at: "2011-07-02 20:04:01", updated_at: "2011-07-02 20:04:01">]
ruby-1.9.2-p180:007:0>> p2.inverse_related_posts
  Post Load (0.2ms)  SELECT "posts".* FROM "posts" INNER JOIN "related_posts" ON "posts"."id" = "related_posts"."post_id" WHERE "related_posts"."related_post_id" = 2
# => [#<Post id: 1, name: "Hello", created_at: "2011-07-02 20:03:43", updated_at: "2011-07-02 20:03:43">]
ruby-1.9.2-p180:008:0>> p = Post.first
  Post Load (0.5ms)  SELECT "posts".* FROM "posts" LIMIT 1
# => #<Post id: 1, name: "Hello", created_at: "2011-07-02 20:03:43", updated_at: "2011-07-02 20:03:43">
ruby-1.9.2-p180:009:0>> p2.related_posts << p
  SQL (25.7ms)  INSERT INTO "related_posts" ("created_at", "post_id", "related_post_id", "updated_at") VALUES (?, ?, ?, ?)  [["created_at", Sat, 02 Jul 2011 20:05:29 UTC +00:00], ["post_id", 2], ["related_post_id", 1], ["updated_at", Sat, 02 Jul 2011 20:05:29 UTC +00:00]]
  Post Load (0.3ms)  SELECT "posts".* FROM "posts" INNER JOIN "related_posts" ON "posts"."id" = "related_posts"."related_post_id" WHERE "related_posts"."post_id" = 2
# => [#<Post id: 1, name: "Hello", created_at: "2011-07-02 20:03:43", updated_at: "2011-07-02 20:03:43">]
ruby-1.9.2-p180:010:0>> p2.related_posts
# => [#<Post id: 1, name: "Hello", created_at: "2011-07-02 20:03:43", updated_at: "2011-07-02 20:03:43">]
ruby-1.9.2-p180:011:0>> exit


Loading development environment (Rails 3.1.0.rc4)
ruby-1.9.2-p180:001:0>> Post.first.related_posts
  Post Load (0.3ms)  SELECT "posts".* FROM "posts" LIMIT 1
  Post Load (0.2ms)  SELECT "posts".* FROM "posts" INNER JOIN "related_posts" ON "posts"."id" = "related_posts"."related_post_id" WHERE "related_posts"."post_id" = 1
# => [#<Post id: 2, name: "World", created_at: "2011-07-02 20:03:48", updated_at: "2011-07-02 20:03:48">]
ruby-1.9.2-p180:002:0>> Post.last.related_posts
  Post Load (0.2ms)  SELECT "posts".* FROM "posts" ORDER BY "posts"."id" DESC LIMIT 1
  Post Load (0.2ms)  SELECT "posts".* FROM "posts" INNER JOIN "related_posts" ON "posts"."id" = "related_posts"."related_post_id" WHERE "related_posts"."post_id" = 2
# => [#<Post id: 1, name: "Hello", created_at: "2011-07-02 20:03:43", updated_at: "2011-07-02 20:03:43">]
like image 128
Dogbert Avatar answered Oct 02 '22 20:10

Dogbert


You're looking for self referential association.

I suggest you take inspiration here.

like image 29
apneadiving Avatar answered Oct 02 '22 20:10

apneadiving