The following test fails on the last line as firm.clients.length == 2,
not 4:
def test_replace_with_duplicates
firm = Firm.find(:first)
c1, c2 = companies(:first_client),companies(:second_client)
assert_equal firm.clients, [c1, c2]
firm.clients = [c1, c1, c2, c2]
firm.save
firm.reload
assert_equal 4, firm.clients.length
end
Looking at the implementation of the "replace" method in
ActiveRecord::Associations::AssociationCollection I can see where this
behavior is coming from. Not what i was expecting really. Is this
expected?
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups
"Ruby on Rails: Talk" group.
To post to this group, send email to
rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org
To unsubscribe from this group, send email to
rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org
For more options, visit this group at
http://groups.google.com/group/rubyonrails-talk?hl=en
-~----------~----~----~----~------~----~------~--~---
Jeff, I wonder if it would make a difference if you changed line 3 to be: c1, c2 = Company.find(1), Company.find(2) I know that I have seen a difference in the past between loading the fixture from the db vs. loading with the fixture method. --Tom On Jan 21, 4:37 pm, jeff b <jeffrey.barr...-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:> The following test fails on the last line as firm.clients.length == 2, > not 4: > > def test_replace_with_duplicates > firm = Firm.find(:first) > c1, c2 = companies(:first_client),companies(:second_client) > assert_equal firm.clients, [c1, c2] > firm.clients = [c1, c1, c2, c2] > firm.save > firm.reload > assert_equal 4, firm.clients.length > end > > Looking at the implementation of the "replace" method in > ActiveRecord::Associations::AssociationCollection I can see where this > behavior is coming from. Not what i was expecting really. Is this > expected?--~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
Hi Tom, I gave the above a shot, but it gave the same results. -J On Jan 21, 1:41 pm, TomRossi7 <t...-5bxIUPmzHicFraO2wh7vUA@public.gmane.org> wrote:> Jeff, > > I wonder if it would make a difference if you changed line 3 to be: > > c1, c2 = Company.find(1), Company.find(2) > > I know that I have seen a difference in the past between loading the > fixture from the db vs. loading with the fixture method. > > --Tom > > On Jan 21, 4:37 pm, jeff b <jeffrey.barr...-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote: > > > The following test fails on the last line as firm.clients.length == 2, > > not 4: > > > def test_replace_with_duplicates > > firm = Firm.find(:first) > > c1, c2 = companies(:first_client),companies(:second_client) > > assert_equal firm.clients, [c1, c2] > > firm.clients = [c1, c1, c2, c2] > > firm.save > > firm.reload > > assert_equal 4, firm.clients.length > > end > > > Looking at the implementation of the "replace" method in > > ActiveRecord::Associations::AssociationCollection I can see where this > > behavior is coming from. Not what i was expecting really. Is this > > expected?--~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
Why would you want to associate the same client to the same firm multiple times anyway? The idea is that for a has_many you have unique records. For example if I had a post and it had a comment on it, if I did @blog.comments= [ Comment.find(:first),Comment.find(:first)] it should only show ONE comment. --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
I agree with you in the case where a domain model expects a unique set of associations, but what about situation that doesn''t? It''s a similar difference between Arrays and Sets: Arrays can be unique or have duplicates, but Sets must be unique. I would understand if it was clear that has_many associations are treated like Sets, but nothing describes them that way. And if they were, what would be the point of the ":uniq" option on has_many and HABTM? It would be redundant, right? On Jan 21, 2:37 pm, "Ryan Bigg" <radarliste...-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:> Why would you want to associate the same client to the same firm multiple > times anyway? The idea is that for a has_many you have unique records. For > example if I had a post and it had a comment on it, if I did @blog.comments= [ > Comment.find(:first),Comment.find(:first)] it should only show ONE comment.--~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
What is the SQL in the test.log telling you about the objects? Is it actually creating two additional clients with the same attributes? If not, I don''t see how it would be possible after the reload for the firm to 4 clients? On Jan 21, 4:41 pm, TomRossi7 <t...-5bxIUPmzHicFraO2wh7vUA@public.gmane.org> wrote:> Jeff, > > I wonder if it would make a difference if you changed line 3 to be: > > c1, c2 = Company.find(1), Company.find(2) > > I know that I have seen a difference in the past between loading the > fixture from the db vs. loading with the fixture method. > > --Tom > > On Jan 21, 4:37 pm, jeff b <jeffrey.barr...-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote: > > > The following test fails on the last line as firm.clients.length == 2, > > not 4: > > > def test_replace_with_duplicates > > firm = Firm.find(:first) > > c1, c2 = companies(:first_client),companies(:second_client) > > assert_equal firm.clients, [c1, c2] > > firm.clients = [c1, c1, c2, c2] > > firm.save > > firm.reload > > assert_equal 4, firm.clients.length > > end > > > Looking at the implementation of the "replace" method in > > ActiveRecord::Associations::AssociationCollection I can see where this > > behavior is coming from. Not what i was expecting really. Is this > > expected?--~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
It is not creating two new clients with the same attributes. The
"replace" method in AssoctiationCollection, which is what is called
when you do something like model_instance.collection = [other_model1,
other_model2], looks like this:
# Replace this collection with +other_array+
# This will perform a diff and delete/add only records that have
changed.
def replace(other_array)
other_array.each { |val| raise_on_type_mismatch(val) }
load_target
other = other_array.size < 100 ? other_array :
other_array.to_set
current = @target.size < 100 ? @target : @target.to_set
@owner.transaction do
delete(@target.select { |v| !other.include?(v) })
concat(other_array.select { |v| !current.include?(v) })
end
end
Certainly if the size of the existing or supplied array is > 100,
it''s
going to ignore duplicates, but also the delete and concat calls,
because they don''t check for duplicate instances of the same object,
will ignore them as well. That means that if @target and other_array
contain the same unique set of objects replace will do nothing. For
example, consider ModelA which has_many ModelB. Assume instance
mod_a.model_bs == [ mod_b ]. If that were the case, the following
line would have no effect:
mod_a.model_bs = [mod_b, mod_b, mod_b, mod_b, mod_b, mod_b, mod_b,
mod_b, mod_b]
Similarly, if mod_a.model_bs already was set to [mod_b, mod_b, mod_b,
mod_b, mod_b, mod_b, mod_b, mod_b, mod_b], and we did the following,
it would have no effect:
mod_a.model_bs = [mod_b]
What I''m suggesting is that if we created the has_many relationship
with :uniq => true I would expect this behavior. Instead it seems
that :uniq => true only has an effect on database reads.
"collection=" is documented in the API as, "replaces the
collections
content by deleting and adding objects as appropriate", which
doesn''t
seem true if the current or new collection contain duplicates
On Jan 21, 5:00 pm, TomRossi7
<t...-5bxIUPmzHicFraO2wh7vUA@public.gmane.org>
wrote:> What is the SQL in the test.log telling you about the objects? Is it
> actually creating two additional clients with the same attributes? If
> not, I don''t see how it would be possible after the reload for the
> firm to 4 clients?
>
> On Jan 21, 4:41 pm, TomRossi7
<t...-5bxIUPmzHicFraO2wh7vUA@public.gmane.org> wrote:
>
> > Jeff,
>
> > I wonder if it would make a difference if you changed line 3 to be:
>
> > c1, c2 = Company.find(1), Company.find(2)
>
> > I know that I have seen a difference in the past between loading the
> > fixture from the db vs. loading with the fixture method.
>
> > --Tom
>
> > On Jan 21, 4:37 pm, jeff b
<jeffrey.barr...-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:
>
> > > The following test fails on the last line as firm.clients.length
== 2,
> > > not 4:
>
> > > def test_replace_with_duplicates
> > > firm = Firm.find(:first)
> > > c1, c2 = companies(:first_client),companies(:second_client)
> > > assert_equal firm.clients, [c1, c2]
> > > firm.clients = [c1, c1, c2, c2]
> > > firm.save
> > > firm.reload
> > > assert_equal 4, firm.clients.length
> > > end
>
> > > Looking at the implementation of the "replace" method
in
> > > ActiveRecord::Associations::AssociationCollection I can see where
this
> > > behavior is coming from. Not what i was expecting really. Is
this
> > > expected?
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups
"Ruby on Rails: Talk" group.
To post to this group, send email to
rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org
To unsubscribe from this group, send email to
rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org
For more options, visit this group at
http://groups.google.com/group/rubyonrails-talk?hl=en
-~----------~----~----~----~------~----~------~--~---
jeff b wrote:> The following test fails on the last line as firm.clients.length == 2, > not 4: > > def test_replace_with_duplicates > firm = Firm.find(:first) > c1, c2 = companies(:first_client),companies(:second_client) > assert_equal firm.clients, [c1, c2] > firm.clients = [c1, c1, c2, c2] > firm.save > firm.reload > assert_equal 4, firm.clients.length > end > > Looking at the implementation of the "replace" method in > ActiveRecord::Associations::AssociationCollection I can see where this > behavior is coming from. Not what i was expecting really. Is this > expected?c1,c1 is the SAME db record, and it''s a primary key in your db.. unless you dup c1 and save with a different id, you will still only have one model in your db with that id. The only way to do the functionality that you desire is by adding in a link table and exploring has and belongs to many relationships as the link table can keep "duplicate" associations.. Forget about rails and look at it from the db side of things.. hth ilan -- Posted via http://www.ruby-forum.com/. --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
ara.t.howard
2008-Jan-24 23:45 UTC
Re: has_many seems funky when handling duplicate records
> > What I''m suggesting is that if we created the has_many relationship > with :uniq=> true I would expect this behavior. Instead it seems > that :uniq=> true only has an effect on database reads. > "collection=" is documented in the API as, "replaces the collections > content by deleting and adding objects as appropriate", which doesn''t > seem true if the current or new collection contain duplicates >yes. if one says class Users < ActiveRecord::Base has_and_belongs_to_many :roles, :uniq => true end then, at minimum, rails should do something like before_save do |record| record.send(:roles).uniq! end under the hood for you. anything else is very non-POLS, to put it mildly. kind regards. --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---