I am having a little problem with relations between models when more than one is created within one action.
I have a Member, User and Address class as defined by the schemas below:
create_table "addresses", :force => true do |t| t.column "location_id", :integer t.column "location_type", :string t.column "city", :string t.column "region_code", :string t.column "address_1", :string t.column "address_2", :string t.column "address_3", :string t.column "address_4", :string t.column "country_id", :integer t.column "province_id", :integer t.column "longitude", :string t.column "latitude", :string end
create_table "members", :force => true do |t| t.column "first_name", :string t.column "last_name", :string t.column "primary_phone", :string t.column "activated", :boolean, :default => false end
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 "member_id", :integer end
And here are the corresponding models (or at least part of them):
class Member < ActiveRecord::Base has_one :user has_one :address, :as => :location
class Address < ActiveRecord::Base belongs_to :province belongs_to :country
validates_presence_of :location_type, :location_id, :address_1, :city, :country_id validates_presence_of :province_id, :if => [1, 2].include? (:country_id)
class User < ActiveRecord::Base belongs_to :member
validates_presence_of :member_id validates_presence_of :login, :email validates_presence_of :password, :if => :password_required? validates_presence_of :password_confirmation, :if => :password_required? validates_uniqueness_of :login, :email, :case_sensitive => false
Here is the Member controller code for the create method:
def create @member = Member.new(params[:member]) @address = Address.new(params[:address]) @user = User.new(params[:user])
@member.address = @address @member.user = @user
respond_to do |format| if @member.address.valid? and @member.user.valid? and @member.save ... end end end
The above controller code will not ever be able to save seeing as address will fail validation due to it missing the location_type and location_id column values for the polymorphic relationship. If I remove the validates_presence_of for the location_id and location_type I then get the error "Member can't be blank", which is caused by the belong_to :member statement within the User class.
Now I could remove the validation check on the address and user models and simply call the save, and that will work if all the models are valid. All inserts will be performed in the proper order and all relations will be created, BUT if there are any validation errors within either the address or user model it will not thrown an error on the member.save! or return a false on member.save.
When member.save is called the Member model will be inserted, but neither the address or user will be inserted as was confirmed when performing through the console.
I have tried to enclose the save within a transaction, but since an error is not raised the transaction if committed and once again the address and user models are not inserted.
Below is some of the console output with ap, mp and up being some predefined address, member and user hashed params:
peter = Member.new(mp)
=> #<Member:0x2400dc4 @new_record=true, @attributes={"activated"=>false, "first_name"=>"john", "last_name"=>"doe", "primary_phone"=>"555-2323"}>
peter.address = Address.new(ap)
=> #<Address:0x23f80c0 @new_record=true, @attributes={"city"=>"Edmonton", "latitude"=>nil, "region_code"=>"T5Z3J4", "province_id"=>1, "country_id"=>1, "location_type"=>"Member", "location_id"=>nil, "address_1"=>"16436-81st", "address_2"=>nil, "longitude"=>nil, "address_3"=>nil, "address_4"=>nil}>
peter.user = User.new(up)
=> #<User:0x23e5aec @password_confirmation="123123", @new_record=true, @password="123123", @attributes={"salt"=>nil, "updated_at"=>nil, "crypted_password"=>nil, "member_id"=>nil, "remember_token_expires_at"=>nil, "remember_token"=>nil, "login"=>"chris1", "created_at"=>nil, "email"=>"chris1@gmail.com"}>
peter.save!
=> true
peter
=> #<Member:0x2400dc4 @errors=#<ActiveRecord::Errors:0x23e01c8 @errors={}, @base=#<Member:0x2400dc4 ...>>, @new_record=false, @address=#<Address:0x23f80c0 @new_record=true, @attributes={"city"=>"Edmonton", "latitude"=>nil, "region_code"=>"T5Z3J4", "province_id"=>1, "country_id"=>1, "location_type"=>"Member", "location_id"=>nil, "address_1"=>"16436-81st", "address_2"=>nil, "longitude"=>nil, "address_3"=>nil, "address_4"=>nil}>, @attributes={"id"=>29, "activated"=>false, "first_name"=>"john", "last_name"=>"doe", "primary_phone"=>"555-2323"}, @user=#<User:0x23e5aec @errors=#<ActiveRecord::Errors:0x23d9c9c @errors={"login"=>["has already been taken"], "email"=>["has already been taken"]}, @base=#<User:0x23e5aec ...>>, @password_confirmation="123123", @new_record=true, @password="123123", @attributes={"salt"=>nil, "updated_at"=>nil, "crypted_password"=>nil, "member_id"=>29, "remember_token_expires_at"=>nil, "remember_token"=>nil, "login"=>"chris1", "created_at"=>nil, "email"=>"chris1@gmail.com"}>>
It can be seen that the user and address were not inserted while the member was.
So why is Rails allowing this to happen and what is the best way to get around this, or am I way off in left field and I am doing things totally wrong?
Thanks for the help.
(I posted a question like this about a week or so ago and got zero responses. I am not sure if this is such a common question that it is now ignored or if no one has an answer. Could someone please let me know either way?)