Strange has_many :through issue with adding objects to a collection
Bounty of $25 for the first working solution.
I'm seeing a really weird has_many :through problem with ActiveRecord.
With these classes:
create_table :numbers do |t|
t.string :phone_number
end
create_table :call_lists do |t|
t.string :type
t.string :name
t.integer :user_id
t.integer :county_id
t.integer :state_id
end
create_table :call_list_memberships do |t|
t.integer :call_list_id
t.intege开发者_如何学JAVAr :number_id
end
class CallList < ActiveRecord::Base
attr_accessible :name
has_many :call_list_memberships, :autosave => true
has_many :numbers, :through => :call_list_memberships, :autosave => true
belongs_to :user
end
class PoliticalDistrict < CallList
end
class CallListMembership < ActiveRecord::Base
belongs_to :call_list
belongs_to :number
end
class Number < ActiveRecord::Base
attr_accessible :phone_number
has_many :call_list_memberships
has_many :call_lists, :through => :call_list_memberships
end
I get this behaviour:
>> p1 = PoliticalDistrict.first
=> #<PoliticalDistrict id: 2, type: "PoliticalDistrict", name: "Random political district", user_id: nil, county_id: nil, state_id: nil>
>> p1.numbers
=> []
>> p1.call_list_memberships
=> []
>> n1 = Number.first
=> #<Number id: 1, phone_number: "07921088939">
>> p1.numbers << n1
=> [#<Number id: 1, phone_number: "07921088939">]
>> p1.numbers
=> [#<Number id: 1, phone_number: "07921088939">]
>> p1.call_list_memberships
=> [#<CallListMembership id: 6, call_list_id: 2, number_id: nil>, #<CallListMembership id: 6, call_list_id: 2, number_id: nil>]
>> p1.save
=> true
Adding an object to the :through collection appears to add two items to the root association, both missing the ID of the object added.
Does anyone have any ideas why this might be happening?
Edit: Even this doesn't work:
>> pd2.call_list_memberships.create :number => Number.first
=> #<CallListMembership call_list_id: 2, number_id: nil>
>> Number.first
=> #<Number id: 1, phone_number: "07921088939">
>> pd2.call_list_memberships
=> [#<CallListMembership call_list_id: 2, number_id: nil>]
Ok, so after a long and rather frustrating search for the answer to this, I finally tracked down the issue and it's a damned embarrassing one.
I was using the following dynamic attr_accesssible code in an initializer:
class ActiveRecord::Base
attr_accessible
attr_accessor :accessible
private
def mass_assignment_authorizer
if accessible == :all
self.class.protected_attributes
else
super + (accessible || [])
end
end
end
Which I had conveniently forgotten about and which caused number_id to be inaccessible (thanks to the empty attr_accessible call).
This is indeed strange behavior. I'm not sure if it is related only to IRB, or to ActiveRecord itself. You'll note that the association is being saved to the database correctly, but the results of IRB calls on the association counts are wrong. Using your example:
>> p1.call_list_memberships
=> [#<CallListMembership id: 6, call_list_id: 2, number_id: nil>, #<CallListMembership id: 6, call_list_id: 2, number_id: nil>]
>> p1.save
>> p1.call_list_memberships.length
=> 2 # Huh?
>> p1.call_list_memberships.size
=> 2 # Wtf?
>> p1.call_list_memberships.count
=> 1 # That's more like it
>> p1.reload # refresh the data
>> p1.call_list_memberships.length
=> 1 # now the associations are correct
>> p1.call_list_memberships.size
=> 1
What's more odd is it repeats the join table row in the association -- check the object id's of the two CallListMemberships and you'll see they are the same.
EDIT
Creating the association using the join table appears to avoid this problem:
>> p1=PoliticalDistrict.create(:name=>"Sample")
=> #<PoliticalDistrict id: 1, type: "PoliticalDistrict", name: "Sample", user_id: nil, county_id: nil, state_id: nil, created_at: "2010-11-14 21:33:09", updated_at: "2010-11-14 21:33:09">
>> n1=Number.create(:phone_number=>"123")
=> #<Number id: 1, phone_number: "123", created_at: "2010-11-14 21:33:22", updated_at: "2010-11-14 21:33:22">
>> p1.call_list_memberships.create(:number=>n1)
=> #<CallListMembership id: 1, call_list_id: 1, number_id: 1, created_at: "2010-11-14 21:33:39", updated_at: "2010-11-14 21:33:39">
>> p1.call_list_memberships
=> [#<CallListMembership id: 1, call_list_id: 1, number_id: 1, created_at: "2010-11-14 21:33:39", updated_at: "2010-11-14 21:33:39">]
>> p1.call_list_memberships.length
=> 1
精彩评论