在Rails 4中给出以下模型:
class Customer < ActiveRecord::Base
has_many :orders
end
class Order < ActiveRecord::Base
belongs_to :customer
end
我可以使用新订单创建新客户,并通过保存父订单同时保存它们:
2.0.0-p247 :001 > customer = Customer.new
2.0.0-p247 :002 > order = customer.orders.build
2.0.0-p247 :003 > customer.save
2.0.0-p247 :004 > order.customer_id
=> 3
这很好用。但是,Rails将允许我只保存订单:
2.0.0-p247 :005 > customer = Customer.new
2.0.0-p247 :006 > order = customer.orders.build
2.0.0-p247 :007 > order.save
2.0.0-p247 :008 > order.customer_id
=> nil
如果我从不致电customer.save
,那么我最终会得到orders.customer_id
为null
的数据库行。
答案似乎是在其客户父母的订单模型中添加validates
行,但我从来没能找到合适的公式。
添加validates :customer, presence: true
没有帮助,因为在上面的示例中,订单确实有客户 - 但它是一个尚未拥有ID的新客户; order.save
调用成功,数据库orders.customer_id
为null
。
添加validates :customer, associated: true
也无济于事; order.save
成功,数据库orders.customer_id
为null
。
添加validates :customer_id, presence: true
会导致order.save
失败 - 但它也会使customer.save
新的子订单成功,因为Rails会检查所有customer.orders
的有效性在customer.save
发生之前。
我知道TDD原则是“测试你自己的代码,而不是别人的代码”,我想要信任Rails来完成它的工作。但似乎没有关闭这个漏洞,我可能会意外地编写保存孤儿订单的代码并最终在我的数据库中使用无效数据。我不想在检查现有order.customer
时丢弃我的代码。
解决这个问题的正确方法是什么?
答案 0 :(得分:2)
我会回滚并将迁移文件更改为不允许null customer_id字段。
class CreateOrders < ActiveRecord::Migration
def change
create_table :orders do |t|
t.integer :customer_id, {null: false} #this would work?
t.timestamps
end
add_index :orders, :customer_id
end
end
答案 1 :(得分:0)
如果您使用order.customer
代替Customer.create
Customer.new