Search code examples
ruby-on-railsassociationsbelongs-to

Has_many collection method returns empty array if I create the child object through another parent object


Sorry for the confusing title, I couldnt explain it better. Feel free to edit it.

I have this structure:

class Home < ActiveRecord::Base
 has_many :reservations

end

class User < ActiveRecord::Base
 has_many :reservations

end

class Reservation < ActiveRecord::Base
 belongs_to :user
 belongs_to :home
end

Which means that Reservations ar associated to the user that makes the reservation, and the home that he is booking.

I am creating the reservations instances like this:

user_instance.reservations.create(:home => home_instance, :dates_and_other_data => ...)

Using this method, the reservation is properly created in the database, and I can retrieve it (with the rest of the reservations the user has made) with

user_instance.reservations

But, if I try to retrieve it from the home instance I used as a parameter, like this:

home_instance.reservations 

This returns an empty array. The opposite happens as well (I cannot retrieve the reservation from the user instance if I create it from the homes).

What am I doing wrong?

Aren´t these two sentences the same?:

Reservation.create(:user => user, :home => home, :other_stuff)
User.reservations.create(:home => home, :other_stuff)

Solution

  • The user model has no way of knowing that you just created a reservation from the home model, and vice versa. When you access an association for the first time, ActiveRecord memoizes the result of that call, so it doesn't need to call the database each time you subsequently use that association.

    home_instance.reservations   # This will pull from the databse, and store the
                                 # results in a variable.
    
    # This will create a new reservation record in the database.
    user_instance.reservations.create(:home => home_instance, :dates_and_other_data => ...)
    
    home_instance.reservations   # We don't go to the database here, since we already
                                 # have the stored variable. So we don't see the new
                                 # reservation.
    

    You can get around this by passing true as an argument to a has_many association, like so:

    home_instance.reservations(true)  # The true forces ActiveRecord to requery the DB>