I m not sure if what I m even trying to do is possible but here goes.
I have an SQL database with the following tables defined (showing only relevant tables in SQL):
CREATE TABLE customers(
id integer NOT NULL UNIQUE,
name vachar(25) NOT NULL,
surname vachar(25) NOT NULL,
password vachar(20) NOT NULL,
email_address vachar(1024) NOT NULL,
home_phone vachar(15),
mobile_phone vachar(15),
office_phone vachar(15),
billing_address_id integer NOT NULL,
postal_address_id integer,
FOREIGN KEY (billing_address_id) REFERENCES addresses(id),
FOREIGN KEY (postal_address_id) REFERENCES addresses(id),
PRIMARY KEY (id));
CREATE TABLE addresses(
id integer NOT NULL UNIQUE,
line1 vachar(100) NOT NULL,
line2 vachar(100),
state vachar(30) NOT NULL,
postcode vachar(10) NOT NULL,
country_id vachar(3) NOT NULL,
PRIMARY KEY (id));
CREATE TABLE orders(
id integer NOT NULL UNIQUE,
customer_id integer NOT NULL UNIQUE,
order_date date NOT NULL,
postal_address_id integer NOT NULL UNIQUE,
FOREIGN KEY (customer_id) REFERENCES customers(id),
PRIMARY KEY (id));
As you can see, the "customers" table defines a one-to-two relationship with addresses (one for billing address and one for postal/shipping address). The idea here being two fold:
- Saves duplicating address fields in the customers table by using relationships to address table.
- Later I can use the address ID to easily fill out the shipping address for an "order".
Now I want to model this using Active Records with rails. So far I have the following:
1) The "Customer" model:
class Customer < ActiveRecord::Base
has_one :postal_address, :class_name => Address , :foreign_key => :postal_address_id
has_one :billing_address, :class_name => Address , :foreign_key => :billing_address_id
accepts_nested_attributes_for :postal_address, :billing_address, :allow_destroy => true
end
2) The address model (default):
class Address < ActiveRecord::Base
end
3) The customer controller (only relevant methods shown, i.e. new & create):
class CustomersController < ApplicationController
# GET /customers/new
# GET /customers/new.xml
def new
@customer = Customer.new
@customer.postal_address = Address.new
@customer.billing_address = Address.new
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => @customer }
end
end
# POST /customers
# POST /customers.xml
def create
@customer = Customer.new(params[:customer])
respond_to do |format|
if @customer.save
flash[:notice] = Customer was successfully created.
format.html { redirect_to(@customer) }
format.xml { render :xml => @customer, :status => :created, :location => @customer }
else
format.html { render :action => "new" }
format.xml { render :xml => @customer.errors, :status => :unprocessable_entity }
end
end
end
end
3) My nested form for creating a new customer with billing address as well.
<% form_for(@customer) do |f| %>
<%= f.error_messages %>
<%= f.label :name, Name: %>
<%= f.text_field :name %>
<%= f.label :surname, Surname: %>
<%= f.text_field :surname %>
<br>
<%= f.label :email_address, Email: %>
<%= f.text_field :email_address %>
<%= f.label :confirm_email_address, Confirm Email: %>
<input id="confirm_email_address" type="text" />
<br>
<%= f.label :password, Password: %>
<%= f.text_field :password %>
<%= f.label :confirm_password, Confirm Password: %>
<input id="confirm_password" type="password" %>
<br>
<%= f.label :home_phone, Home Phone: %>
<%= f.text_field :home_phone %>
<%= f.label :mobile_phone, Mobile Phone: %>
<%= f.text_field :mobile_phone %>
<%= f.label :office_phone, Office Phone: %>
<%= f.text_field :office_phone %>
<br>
<% f.fields_for :billing_address do |billing_form| %>
<%= billing_form.label :line1, Billing Address: %>
<%= billing_form.text_field :line1 %>
<br>
<%= billing_form.text_field :line2 %>
<br>
<%= billing_form.label :state, State / Province / Region: %>
<%= billing_form.text_field :state %>
<br>
<%= billing_form.label :postcode, Postcode / ZIP: %>
<%= billing_form.text_field :postcode %>
<br>
<%= billing_form.label :country_id, Country: %>
<%= billing_form.text_field :country_id %>
<% end %>
<p>
<%= f.submit Create %>
</p>
<% end %>
Now to the problem. When I fill out this form and proceed to creating the new record I get the following error:
SQLite3::SQLException: customers.billing_address_id may not be NULL: INSERT INTO "customers" ("name", "office_phone", "billing_address_id", "postal_address_id", "home_phone", "surname", "password", "email_address", "mobile_phone") VALUES( Michael , , NULL, NULL, 93062145 , Fazio , 9npn4zicr , michael.fazio@me.com , )
From this I understand that the billing address is not being created before the customer. I thought (probably very naively) that active record would recognize the relationship between a customer and address record and do the correct operation to create the new records. This is obviously not the case.
How can I make this so? I m assuming logic needs to be in the customer controller to save the address record first then get the ID for that record to use in the customer controller. All within a transaction? Or maybe I have just modeled my DB in a bad way?
Hope that all this code was not too much but I wanted to give as much context as possible.