开发者

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
0

上一篇:

下一篇:

精彩评论

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

最新问答

问答排行榜