problem with has_many :through association

I have the following models

   1. class Caregiver < ActiveRecord::Base    2. has_many :call_orders    3. has_many :users, :through => :call_orders    4. end    5.    6. class User < ActiveRecord::Base    7. has_many :call_orders, :order => :position    8. has_many :caregivers, :through => :call_orders    9. end   10.   11. class CallOrder < ActiveRecord::Base   12. belongs_to :user   13. belongs_to :caregiver   14. acts_as_list :scope => :user   15. end

When I run the following commands, I get an error:

   1. @call_list = User.find(params[:id])    2. @call_list.call_orders.each do |call_order|    3. puts call_order.caregiver.first_name

This is the error:

   You have a nil object when you didn't expect it!    The error occurred while evaluating nil.first_name

chirag wrote:

I have the following models

   1. class Caregiver < ActiveRecord::Base    2. has_many :call_orders    3. has_many :users, :through => :call_orders    4. end    5.    6. class User < ActiveRecord::Base    7. has_many :call_orders, :order => :position    8. has_many :caregivers, :through => :call_orders    9. end   10.   11. class CallOrder < ActiveRecord::Base   12. belongs_to :user   13. belongs_to :caregiver   14. acts_as_list :scope => :user   15. end

When I run the following commands, I get an error:

   1. @call_list = User.find(params[:id])    2. @call_list.call_orders.each do |call_order|    3. puts call_order.caregiver.first_name

This is the error:

   You have a nil object when you didn't expect it!    The error occurred while evaluating nil.first_name

What do your tables/migrations look like?

class CreateCaregivers < ActiveRecord::Migration   def self.up     create_table :caregivers do |t|       t.column :first_name, :string       t.column :last_name, :string       t.column :address, :string       t.column :city, :string       t.column :state, :string       t.column :home_phone, :string       t.column :work_phone, :string       t.column :cell_phone, :string       t.column :relationship, :string       t.column :email, :string     end   end

  def self.down     drop_table :caregivers   end end

class CreateCallOrders < ActiveRecord::Migration   def self.up     create_table :call_orders do |t|       t.column :user_id, :integer       t.column :caregiver_id, :integer       t.column :position, :integer     end   end

  def self.down     drop_table :call_orders   end end

class CreateUsers < ActiveRecord::Migration   def self.up     create_table "users", :force => true do |t|       t.column :login, :string       t.column :email, :string       t.column :crypted_password, :string, :limit => 40       t.column :salt, :string, :limit => 40       t.column :created_at, :datetime       t.column :updated_at, :datetime       t.column :remember_token, :string       t.column :remember_token_expires_at, :datetime

      t.column :activation_code, :string, :limit => 40       t.column :activated_at, :datetime     end   end

  def self.down     drop_table "users"   end end

Thanks for the help! Chirag

Migrations look fine and it's working for me:

>> call_list = User.find(1) => #<User:0x3447480 @attributes={"salt"=>nil, "activated_at"=>nil, "updated_at"=>"2007-10-03 22:25:01", "crypted_password"=>nil, "activation_code"=>nil, "remember_token_expires_at"=>nil, "id"=>"1", "remember_token"=>nil, "login"=>"test", "created_at"=>"2007-10-03 22:25:01", "email"=>nil}> >> call_list.call_orders.each do |call_order| ?> puts call_order.caregiver.first_name >> end John

>> call_list.call_orders[0].caregiver => #<Caregiver:0x3441634 @attributes={"work_phone"=>nil, "city"=>nil, "id"=>"1", "relationship"=>nil, "cell_phone"=>nil, "home_phone"=>nil, "first_name"=>"John", "address"=>nil, "last_name"=>"Smith", "email"=>nil, "state"=>nil}>

My guess is you have some bad data somewhere.

Im agree with Michael.

Btw, you can improve your code with a nested :include option like this: @call_list = User.find(params[:id], :include => [ { :call_orders => :caregiver } ])

Take a look at this: http://snippets.dzone.com/posts/show/2089 for a better explanation.

Regards. Pablo Castellazzi.

Is that just the way the data is? caregiver is belongs_to of callorder: call_order.caregiver can be nil (unless you've got other contraints that forbid it)

Fred

Is that just the way the data is? caregiver is belongs_to of callorder: call_order.caregiver can be nil (unless you've got other contraints that forbid it)

Freederick mades a good point here. If nil values are accepted as your model definition suggests, then you should use a "compacted" list of caregivers like:

@call_list.call_orders.collect(&:caregiver).compact.each do | caregiver>   puts caregiver.first_name end

if nil values are not accepted you should add valitadates_presence_of and validates_associated to force a caregiver to be present:

class CallOrder < ActiveRecord::Base    belongs_to :user    belongs_to :caregiver    acts_as_list :scope => :user

   validates_presence_of :caregiver_id    validates_associated :caregiver end

Regards Pablo Castellazzi.