开发者

In has_many :through, what is the correct way to create object inheritance

I've hit something that I don't understand how to model with Rails associations and neither STI nor polymorphism seem to address it.

I want to be able to access attributes from a join table via the collection that's created by has_many :through.

In the code below, this means that I want to be able to access the name and description of a committee position via the objects in the .members collection but as far as I can see I can't do that. I have to go through the original join table.

e.g. modelling a club and it's committee members

class User < ActiveRecord::Base
  attr_accessible :full_name, 
                  :email
  has_many: committee_positions
  has_many: committees, :through => committee_positions
end

class Committee < ActiveRecord::Base
  attr_accessible :name

  has_many :committee_positions
  has_many :members, :through => :committee_positions
end

class CommitteePosition < ActiveRecord::Base
  attr_accessible :user_id, 
                  :committee_id, 
                  :member_description, 
                  :role_title

  belongs_to :committee
  belongs_to :user
end

Assume that each committee position instance has a unique description i.e. the description is particular to both the member and the committee and so has to be stored on the join table and not with either the user or the club.

e.g.

Committee member:  Matt Wilkins
Role:             "Rowing club president"
Description:      "Beats the heart of the rowing club to his own particular drum"

Is there a way to access the data in the join table via the committee.members collection?

While active record gives us this great alias for going directly to the members, there doesn't seem to be any way to access the data on the join table that created the collection:

I cannot do the following:

rowing_committee.members.find_by_role_title('president').name

Each item in the .members collection is a user object and doesn't seem to have access to either the role or description that's stored in the CommitteePositions join table.

The only way to do this would be:

rowing_committee.committee_positions.find_by_role_title('president').user.name

This is perfectly do-able but is clunky and unhelpful. I feel like the use-case is sufficiently gene开发者_运维知识库ric that I may well be missing something.

What I would like to access via objects in the committee.members collection

member
- full_name
- email
- role_title (referenced from join table attributes)
- member_description (referenced from join table attributes)

This is only a small thing but it feels ugly. Is there a clean way to instruct the "member" objects to inherit the information contained within the join table?

-------------- addendum

On working through this I realise that I can get half way to solving the problem by simply defining a new class for committee member and referencing that instead of user in the has_many :through relationship. It works a little bit better but is still pretty clunky

class Committee < ActiveRecord::Base
  ...

  has_many :committee_positions
  has_many :members, 
           :through => :committee_positions, 
           :class_name => 'CommitteeMember'
  ...
end


class CommitteeMember < User

  def description( committee )
    self.committees.find_by_committee_id( committee.id ).description
  end

  def role( committee )
    self.committees.find_by_committee_id( committee.id ).description
  end
end

Now this is getting closer but it still feels clunky in that the code to use it would be:

committee = Committee.first
president_description = committee.members.find_by_title('president').description( committee )

Is there any way to initialize these objects with the committee they are referencing?


I think you could use some delegation here. In your Committee_Position class:

class Committee_Position < ActiveRecord::Base
  attr_accessible :user_id, 
                :committee_id, 
                :member_description, 
                :role_title

belongs_to :committee
belongs_to :user

delegate :name, :email, :to => :user
end

so you could do what you say you want:

rowing_club.committee_members.find_by_role_title('president').name
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜