如何防止“孤儿”儿童模型被保存?

时间:2013-11-16 18:31:59

标签: ruby-on-rails activerecord

在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_idnull的数据库行。

答案似乎是在其客户父母的订单模型中添加validates行,但我从来没能找到合适的公式。

  1. 添加validates :customer, presence: true没有帮助,因为在上面的示例中,订单确实有客户 - 但它是一个尚未拥有ID的新客户; order.save调用成功,数据库orders.customer_idnull

  2. 添加validates :customer, associated: true也无济于事; order.save成功,数据库orders.customer_idnull

  3. 添加validates :customer_id, presence: true会导致order.save失败 - 但它也会使customer.save新的子订单成功,因为Rails会检查所有customer.orders的有效性在customer.save发生之前。

  4. 我知道TDD原则是“测试你自己的代码,而不是别人的代码”,我想要信任Rails来完成它的工作。但似乎没有关闭这个漏洞,我可能会意外地编写保存孤儿订单的代码并最终在我的数据库中使用无效数据。我不想在检查现有order.customer时丢弃我的代码。

    解决这个问题的正确方法是什么?

2 个答案:

答案 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