[英]Ruby Rails Nested Records
我不确定我什至试图做的事是否可行,但这是可行的。
我有一个定义了以下表的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));
正如你所看到的,“客户”表中定义了地址(一个计费地址和一个邮政/送货地址)一个一对一2间的关系。 这里的想法有两个方面:
现在,我想使用带轨道的Active Records对此建模。 到目前为止,我有以下内容:
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)客户控制者(仅显示相关方法,即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)我的嵌套表格也用于创建具有帐单地址的新客户。
<% 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,以便在客户控制器中使用。 全部都在交易中吗? 还是我刚刚以一种不好的方式对数据库建模?
希望所有这些代码不要太多,但是我想提供尽可能多的上下文。
第二回合:
好的,希望现在对您有所帮助。 在一个表中实现双重地址的方式并不完全是“轨道”方式。 总的来说,如果您想做某些事情,例如DHH,就必须要做。 因此,rails具有STI( 单表继承 ),您可以在其中拥有一个超类,并从中继承许多类。
在您的情况下,移动这种范例不应做太多工作(我希望)。
步骤1:在盒子上切一个洞
步骤2:更新您的迁移文件。 您希望地址表具有对应于客户的键。 然后删除“客户”表中的billing_address_id和shipping_address_id列,因为我们不再需要这些列。
您还想添加一个名为type
的字段(如果已经使用type,则可以解决)。 像这样:
create_table :addresses do |t|
t.string :line1
t.string :line2
t.string :state
t.integer :postcode
t.integer :country_id
t.integer :customer_id
t.integer :type
t.timestamps
步骤3:更新模型。 更改您的客户类别,如下所示:
class Customer < ActiveRecord::Base
has_one :postal_address
has_one :billing_address
accepts_nested_attributes_for :postal_address, :billing_address, :allow_destroy => true
然后,您将要在models目录中创建两个新文件:billing_address.rb和postal_address.rb。 它们应如下所示:
class BillingAddress < Address
belongs_to :customer
end
class PostalAddress < Address
belongs_to :customer
end
步骤4:更新控制器。 现在,您在问题中显示的唯一控制器是customer_controller.rb,但是,可以在任何地方使用。 您要用实例化“送货地址”或“帐单地址”的调用替换Address.new
。
def new
@customer = Customer.new
@customer.postal_address = PostalAddress.new
@customer.billing_address = BillingAddress.new
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => @customer }
end
end
希望这确实有效,并且可以弥补我之前的糟糕尝试;)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.