Rails multiple belongs_to assignment
Asked Answered
H

1

10

Given

User:

class User < ActiveRecord::Base
   has_many :discussions
   has_many :posts
end

Discussions:

class Discussion < ActiveRecord::Base
    belongs_to :user
    has_many :posts
end

Posts:

class Post < ActiveRecord::Base
    belongs_to :user
    belongs_to :discussion 
end

I am currently initializing Posts in the controller via

@post = current_user.posts.build(params[:post])

My question is, how do I set/save/edit the @post model such that the relationship between the post and the discussion is also set?

Hardcastle answered 18/7, 2012 at 10:18 Comment(0)
L
13

Save and edit discussions along with post

Existing Discussion

To associate the post you're building with an existing discussion, just merge the id into the post params

@post = current_user.posts.build(
          params[:post].merge(
            :discussion_id => existing_discussion.id
        ) 

You will have to have a hidden input for discussion id in the form for @post so the association gets saved.


New Discussion

If you want to build a new discussion along with every post and manage its attributes via the form, use accepts_nested_attributes

class Post < ActiveRecord::Base
  belongs_to :user
  belongs_to :discussion
  accepts_nested_attributes_for :discussion
end

You then have to build the discussion in the controller with build_discussion after you built the post

@post.build_discussion

And in your form, you can include nested fields for discussions

form_for @post do |f|
  f.fields_for :discussion do |df|
    ...etc


This will create a discussion along with the post. For more on nested attributes, watch this excellent railscast


Better Relations

Furthermore, you can use the :through option of the has_many association for a more consistent relational setup:

class User < ActiveRecord::Base
  has_many :posts
  has_many :discussions, :through => :posts, :source => :discussion
end

class Discussion < ActiveRecord::Base
  has_many :posts
end

class Post < ActiveRecord::Base
  belongs_to :user
  belongs_to :discussion 
end

Like this, the relation of the user to the discussion is maintained only in the Post model, and not in two places.

Lesley answered 18/7, 2012 at 12:36 Comment(7)
Wow, that was a great read. Thanks for your time. I do have one question though. Every time the user starts a discussion they will have to have a post, however the reverse is not necessarily true. As in my case where a Discussion consists of many posts, would it be appropriate to flip what you said about posts and discussions such that the Discussions accepts nested attributes for posts, then do a @discussion.build_post, or is there some inconsistency that will come with that?Hardcastle
You're welcome. And yes, if the relationship is User has many Discussions has many Posts, then you should reflect it that way in your code.Lesley
by the way... are you familiar with the belongs_to polymorphic: true element?Hardcastle
Yeah, its up a bit of a different alley but still related. As with the prior code, discussions also has a belongs_to polymorphic: true. This is because a "Discussion" in my case is a general forum thread, and thus I would like to put it on other models. However the discussion would only be directly related to one of those at a time. In such a case would I still be fine using the accepts_nested_attributes_for and then building the discussion on those models? Or would you suggest another approach?Hardcastle
You may have to change some code if you want to use accepts_nested_attributes_for on the polymorphic association, but it should work fine. Since there's no other approach to polymorphic associations than to use polymorphism :)Lesley
let us continue this discussion in chatHardcastle
Do i have to make discussion_id attr_accessible in order to user this method? If that is the case, is there a way to do this without making it accessible?Chessboard

© 2022 - 2024 — McMap. All rights reserved.