问题
I was considering adding some extra properties between a has_many relationship.
For example, I have a Users table, and a Group table. Users can join groups via a :through has_many relationship. I want to add the property of the 'role' of the user in that group.
create_table "groupization", :force => true do |t|
t.integer "user_id"
t.integer "group_id"
t.string "role"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
I was wondering, how can I access the role attribute. I was thinking something like:
user.groups[0].role
Is that a correct approach? I know the syntax is wrong (I tried it); what would the correct syntax be like? Thanks!
回答1:
In your User model, you'll have something like:
class User < ActiveRecord::Base
has_many :groupizations
has_many :groups, :through => :groupizations
end
You'd access the role information just through the has_many
relationship to groupizations
like this in the console.
foo = User.first
bar = foo.groupizations.first
bar.role
Assuming you had groupizations on the first user.
Getting a specific user/group relationship could be done like this:
Groupizations.where("group_id = ? and user_id = ?", group.id, user.id)
Then from there you could get the role you were looking for.
回答2:
I don’t think there’s an elegant way to do this, just something like this:
user.groupizations.find_by_group_id(user.groups[0].id).role
The example code you gave with the [0]
doesn’t seem like a realistic use, so if you showed us how you’d actually be wanting to access role
, there might be a cleaner way.
This was discussed here.
回答3:
The best approach to this is to work with the join model and then delegate/proxy methods to the group, e.g:
class User < ActiveRecord::Base
has_many :memberships, :include => :group, :dependent => :delete_all
has_many :groups, :through => :memberships
end
class Group < ActiveRecord::Base
has_many :memberships, :include => user, :dependent => :delete_all
has_many :users, :through => :memberships
end
class Membership < ActiveRecord::Base
belongs_to :user
belongs_to :group
def group_name
group && group.name
end
def user_name
user && user.name
end
end
Then if you were to display a user's groups in a view:
<ul>
<% @user.memberships.each do |membership| %>
<li><%= membership.group_name %> (<%= membership.role %>)</li>
<% end %>
</ul>
similarly for a group's users:
<ul>
<% @group.memberships.each do |membership| %>
<li><%= membership.user_name %> (<%= membership.role %>)</li>
<% end %>
</ul>
This way only takes three queries - one each for the user, groups and memberships.
回答4:
It's not clear what you are looking for exactly, so I'll throw out another example with the console output to make it more obvious what it happening. Point is, however, that to get just the role(s) for a user, you'd need to specify which of the user's groups you mean (as shown by @BuckDoyle, although he just picks the first one to mirror your example). To get a list of all a user's roles, you need to iterate over the user's groupizations.
1.9.3-p194 :050 > User.find_by_name("Homer").groupizations.each {|groupization|
puts groupization.group.org + ': ' + groupization.role}
User Load (2.8ms) SELECT "users".* FROM "users" WHERE "users"."name" = 'Homer' LIMIT 1
Groupization Load (1.5ms) SELECT "groupizations".* FROM "groupizations" WHERE
"groupizations"."user_id" = 2
Group Load (1.9ms) SELECT "groups".* FROM "groups" WHERE "groups"."id" = 1
LIMIT 1
The Movementarians: cult follower
Group Load (1.4ms) SELECT "groups".* FROM "groups" WHERE "groups"."id" = 2
LIMIT 1
Nuclear Plant: employee
One last thought (kind of a trivial one) "groupizations" certainly works, but if "role" is the only attribute you are going to add, naming the relationship model more concretely (say "Memberships") has a practical benefit in that it makes the relationships a little clearer, at least in my experience.
来源:https://stackoverflow.com/questions/10568393/access-extra-attributes-in-join-table-when-using-though-in-has-many-relationship