Brian McDonough
Brian McDonough

Reputation: 14479

Complex has_many through association

This is a brain teaser for me, but hoping it's clear to someone more experienced. Having trouble sorting out the correct associations.

I have three models: User, Recipient, Discussion

Right now the associations are set up this way:

Discussion

belongs_to :user
has_many :recipients

User

has_many :discussions, dependent: :destroy
has_many :discussions, :through => :recipients

Recipient

belongs_to :user, dependent: :destroy
belongs_to :discussion, dependent: :destroy

When I try to create a discussion with this action in the discussions_controller:

def create
  @discussion = current_user.discussions.build(params[:discussion])
  @discussion.sent = !!params[:send_now]

  if params[:subscribe_to_comments]
    CommentSubscriptionService.new.subscribe(@discussion, current_user)
  end

  if @discussion.save
    redirect_to @discussion, notice: draft_or_sent_notice
  else
    render :new
  end
end

I get this error:

Could not find the association :recipients in model User

I have not yet created the action for saving recipients.

Hoping your answer will help clear the cobwebs for this first issue, which is the association, then I'll move on to the next. Any suggestions are welcome.

Upvotes: 1

Views: 108

Answers (2)

Ryan Endacott
Ryan Endacott

Reputation: 9172

Another potential solution would be to outline your models like this:

class Discussion
  has_many :participants
  has_many :users, :through => :participants

  def leaders
     users.where(:leader => true) # I think this should work: http://www.tweetegy.com/2011/02/setting-join-table-attribute-has_many-through-association-in-rails-activerecord/
  end
end


class Participant
  belongs_to :user
  belongs_to :discussion
  # This class can have attributes like leader, etc.
end

class User
  has_many :participants
  has_many :discussions, :through => :recipients

  def leader?(discussion)
    participants.find_by(:discussion_id => discussion.id).leader? # doesn't seem super elegant
end

With this solution, all of the users are kept together as participants in the discussion, rather than having one leader with multiple recipients. After implementing some of it though, I'm not sure how neat it turned out :P I'll go ahead and post it, but you should make the informed decision yourself.

I'm no expert; this is just another alternative to how you have your models laid out. Let me know if you have any questions. I hope this helps!

Upvotes: 1

Ryan Endacott
Ryan Endacott

Reputation: 9172

It looks like the error is correct; you're missing the recipients association in the User model.

Your user model needs to know about the recipient model in order to use has_many :through

Try adding this to your user model:

has_many :recipients

Edit: Actually, from your question, I'm not entirely sure how you want the models to be laid out. You should also only be calling has_many :discussions once in your user model.

How are your tables laid out? Did you mean to do this for User: has_many :recipients, :through => :discussions?

Edit 2:

Alright, from your comment, I don't think that the user needs to have many recipients. So on a basic level, just remove the second line to make your user model look like:

has_many :discussions, dependent: :destroy

You may also need to remove the belongs_to :user in your recipient model.

Upvotes: 1

Related Questions