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”=>“[email protected]”}>
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”=>“[email protected]”}>>
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?)