This project is archived and is in readonly mode.
Iterate over :has_many array with the join (:through) model
Reported by Daniel Lunde | May 1st, 2008 @ 05:28 PM
It is often the case when I iterate over an array of objects in a has_many_through association that I want access to both the :has_many objects and the :through objects. This patch makes that easier.
Example:
class User < ActiveRecord::Base
has_many :memberships
has_many :groups, :through => :memberships
end
user.groups.each_with_join do |group, membership|
puts "#{group.name}, #{membership.status}"
end
This also works regardless of whether the :through association is joined by a :has_many or :belongs_to.
Comments and changes to this ticket
-
Michael Koziarski May 1st, 2008 @ 09:10 PM
- State changed from new to invalid
This should really just be:
user.memberships.each do |membership|
puts membership.status, membership.group.name
end
So I can't the need for this feature.
-
Daniel Lunde May 1st, 2008 @ 09:22 PM
What about the cases where you are sorting by groups
class User < ActiveRecord::Base has_many :memberships has_many :groups, :through => :memberships, :order => "groups.name" end user.groups.each_with_join do |group, membership| puts "#{group.name}, #{membership.status}" end
By only doing user.memberships, there's no knowledge of the groups to order it correctly.
There's other instances where it makes sense to iterate over the groups rather then the memberships.
-
Pratik May 1st, 2008 @ 10:33 PM
Why can't you just do the following ?
user = User.find(:first, :include => { :groups => :membership }) user.groups.each do |group| puts "#{group.name}, #{group.membership.status}" end
-
Daniel Lunde May 2nd, 2008 @ 04:14 PM
Pratik:
It seems like the obvious thing to do, but there's a problem with it. Let me give a more complete example.
>> pp frank.groups [#<Group id: 2, name: "Swim Team">] >> pp frank.memberships [#<Membership id: 4, user_id: 2, group_id: 2, status: "Starter">] >> pp john.groups [#<Group id: 2, name: "Swim Team">] >> pp john.memberships [#<Membership id: 6, user_id: 3, group_id: 2, status: "Finisher">] # Note, I'm using memberships instead of membership since the Group class declares "has_many :memberships" >> frank = User.find(2, :include => { :groups => :memberships }) >> frank.groups.each do |group| ?> pp group >> pp group.memberships >> end #<Group id: 2, name: "Swim Team"> [#<Membership id: 4, user_id: 2, group_id: 2, status: "Starter">, #<Membership id: 6, user_id: 3, group_id: 2, status: "Finisher">]
So when iterating over frank's groups, it returned not only frank's membership, but all memberships from that group. Which is incorrect.
With my patch, you only get the correct membership record that matches both the user and the group, plus you get it as a record that's not wrapped in an array.
And in those instances where someone might have multiple memberships to a single group, it does give it back as an array of just those memberships.
-
Daniel Lunde May 2nd, 2008 @ 04:42 PM
Here's a real world example where I've needed to use each_with_join:
class Feature < ActiveRecord::Base has_many :featured_items, :dependent => :destroy, :order => 'featured_items.position' has_many :featured_users, :source => :featureable, :through => :featured_items, :source_type => 'User', :order => 'featured_items.position' has_many :featured_media, :source => :featureable, :through => :featured_items, :source_type => 'Medium', :order => 'featured_items.position' end # in view <% feature = Feature.find_by_group("users", :include => :featured_users, :order => 'featured_items.position') %> <% feature.featured_users.each_with_join do |user, featured_item| %> <%= image_tag(featured_item.photo.public_filename) %><br/> <%= user.name %> <% end %>
In this case I couldn't do:
feature.featured_items do |featured_item| featured_item.featured_user.name # featured_user is not a valid method end
Wouldn't you say that using each_with_join in this example would be easier then alternative methods?
-
Michael Koziarski May 4th, 2008 @ 01:25 AM
I certainly can see cases where this is required, but I'm just not convinced that it justifies a new method for us to have to maintain on an ongoing basis.
If there's a join model, it should be exposed.
Create your profile
Help contribute to this project by taking a few moments to create your personal profile. Create your profile »
<h2 style="font-size: 14px">Tickets have moved to Github</h2>
The new ticket tracker is available at <a href="https://github.com/rails/rails/issues">https://github.com/rails/rails/issues</a>