私がやろうとしていることさえ可能かどうかはわかりませんが、ここに行きます。
次のテーブルが定義されたSQLデータベースがあります(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));
ご覧のとおり、「顧客」テーブルは、住所との1対2の関係を定義します(1つは請求先住所用、もう1つは郵便/配送先住所用)。ここでの考え方は2つあります。
- リレーションシップを使用してテーブルをアドレス指定することにより、重複するアドレスフィールドをcustomersテーブルに保存します。
- 後で、住所IDを使用して、「注文」の配送先住所を簡単に入力できます。
次に、レール付きのActiveRecordsを使用してこれをモデル化します。これまでのところ、私は次のものを持っています:
1)「顧客」モデル:
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)アドレスモデル(デフォルト):
class Address < ActiveRecord::Base
end
3)カスタマーコントローラー(関連するメソッドのみが表示されます。つまり、新規および作成):
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)請求先住所も含む新しい顧客を作成するためのネストされたフォーム。
<% 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 %>
今問題に。このフォームに記入して新しいレコードの作成に進むと、次のエラーが発生します。
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', '')
このことから、請求先住所は顧客の前に作成されていないことがわかります。私は(おそらく非常に素朴に)アクティブレコードが顧客と住所レコードの関係を認識し、新しいレコードを作成するために正しい操作を行うと思いました。これは明らかにそうではありません。
どうすればこれを実現できますか?最初にアドレスレコードを保存してから、カスタマーコントローラーで使用するそのレコードのIDを取得するために、ロジックがカスタマーコントローラーにある必要があると想定しています。すべてトランザクション内ですか?それとも、DBを悪い方法でモデル化したばかりですか?
このすべてのコードが多すぎないことを願っていますが、私はできるだけ多くのコンテキストを提供したいと思いました。