Topic: has_many: through data creation question

Hello All,
  I am very new to rails and I have built a couple of tutorials so I am finally moving onto my own project.  I am running into a snag with the has_many :through syntax and creating some test data.

[code=table design]    #User table.
    create_table :users do |t|
      t.column :name, :string
      t.column :login, :string
      t.column :email, :string
      t.column :password, :string
    end
   
    #Addresses.
    create_table :addresses do |t|
      t.column :addr1, :string
      t.column :addr2, :string
      t.column :city, :string
      t.column :state, :string
      t.column :zip, :string
    end

    #User Address, join table.
    create_table :locations do |t|
      t.column :user_id, :integer
      t.column :address_id, :integer
      t.column :added, :datetime
      t.column :name, :string
    end[/code]

class Address < ActiveRecord::Base
  has_many :locations
  has_many :users, :through => :locations
end

class User < ActiveRecord::Base
  has_many :locations
  has_many :addresses, :through => :locations
end

class Location < ActiveRecord::Base
  belongs_to :user
  belongs_to :address
end


I would like to create some test data in my migration file, but I am running into problems.  Currently the only way I have found to create the data is by using the following lines

[code=Creation Code]    admin = User.create(:name=>"admin", :login=>"admin", :email=>"555@info.com", :password=>"test")
    phone = Phone.create(:recordDate => Time.now, :updateDate=>Time.now, :context=>"Email", :name=>"Admin Email", :value=>"666@info.com")
    contact = Contact.create(:user_id=>admin.id, :phone_id=>phone.id, :added=>Time.now, :active=>true)[/code]
This works and I can retrive values, but there has to be a better way to create this data.  When I try using a syntax like

[code=Non Working Code]    admin = User.create(:name=>"admin", :login=>"admin", :email=>"555@info.com", :password=>"test")
    phone = Phone.create(:recordDate => Time.now, :updateDate=>Time.now, :context=>"Email", :name=>"Admin Email", :value=>"666@info.com")
    admin.phones << phone[/code]
I get no error, but the relationship is not saved. 

Output wrote:

>>     admin = User.create(:name=>"admin", :login=>"admin", :email=>"555@info.com", :password=>"test")
=> #<User:0x237370c @errors=#<ActiveRecord::Errors:0x2372668 @errors={}, @base=#<User:0x237370c ...>>, @attributes={"name"=>"admin", "id"=>3, "password"=>"test", "login"=>"admin", "email"=>"555@info.com"}, @new_record_before_save=true, @new_record=false>
>>     phone = Phone.create(:recordDate => Time.now, :updateDate=>Time.now, :context=>"Email", :name=>"Admin Email", :value=>"666@info.com")
=> #<Phone:0x235a2c0 @errors=#<ActiveRecord::Errors:0x23580d8 @errors={}, @base=#<Phone:0x235a2c0 ...>>, @attributes={"name"=>"Admin Email", "id"=>2, "updateDate"=>Sat Dec 16 23:11:25 EST 2006, "value"=>"666@info.com", "context"=>"Email", "recordDate"=>Sat Dec 16 23:11:25 EST 2006}, @new_record_before_save=true, @new_record=false>
>>     admin.phones << phone
=> [#<Phone:0x235a2c0 @errors=#<ActiveRecord::Errors:0x23580d8 @errors={}, @base=#<Phone:0x235a2c0 ...>>, @attributes={"name"=>"Admin Email", "id"=>2, "updateDate"=>Sat Dec 16 23:11:25 EST 2006, "value"=>"666@info.com", "context"=>"Email", "recordDate"=>Sat Dec 16 23:11:25 EST 2006}, @new_record_before_save=true, @new_record=false>]
>> admin.phones.size
=> 1
>> admin.save!
=> true
>> admin = User.find(3)
=> #<User:0x2345780 @attributes={"name"=>"admin", "id"=>"3", "password"=>"test", "login"=>"admin", "email"=>"555@info.com"}>
>> admin.phones.size
=> 0

Any advice would be much appreciated.  I am sure I am doing something stupid, so thank you very much in advance.

Re: has_many: through data creation question

Try calling reset_column_information on all the objects.

User.reset_column_information
Address.reset_column_information
Location.reset_column_information

Rails will initialize your objects and cache them before you create the tables.  reset_column_information will refresh them with the new column information.

Re: has_many: through data creation question

I added those steps to my migration before I created the new objects.  I am still not getting an error but I still am not getting the reference row saved:


    User.reset_column_information
    Address.reset_column_information
    Location.reset_column_information
   
    admin = User.create(:name=>"admin", :login=>"admin", :email=>"555@info.com", :password=>"test")
    phone = Phone.create(:recordDate => Time.now, :updateDate=>Time.now, :context=>"Email", :name=>"Admin Email", :value=>"666@info.com")
    admin.phones << phone

DB Results wrote:

mysql> select * from users;
+----+-------+-------+--------------+----------+
| id | name  | login | email        | password |
+----+-------+-------+--------------+----------+
| 1  | admin | admin | 555@info.com | test     |
+----+-------+-------+--------------+----------+
1 row in set (0.00 sec)

mysql> select * from locations;
Empty set (0.00 sec)

mysql> select * from phones;
+----+---------------------+---------------------+---------+-------------+--------------+
| id | recordDate          | updateDate          | context | name        | value        |
+----+---------------------+---------------------+---------+-------------+--------------+
| 1  | 2006-12-17 01:06:09 | 2006-12-17 01:06:09 | Email   | Admin Email | 666@info.com |
+----+---------------------+---------------------+---------+-------------+--------------+
1 row in set (0.01 sec)

Re: has_many: through data creation question

Oh right, I forgot.  On a has_many through the << operator doesn't quite work how you'd expect it to.  By creating a has many through relationship, you're saying that the join object should have other attributes, and therefore shouldn't be created automatically.  You'll have to create the join object manually.

try something like this

admin.locations << Location.new { |l| l.address = address }

Re: has_many: through data creation question

ah, thanks alot! Finally managed to use it properly big_smile

Should be like this though, I guess

admin.locations << Location.create(:address => address )

I think new needs an explicit call of save