【问题标题】:How do I prevent "orphaned" child models from being saved?如何防止“孤立”子模型被保存?
【发布时间】:2013-11-16 18:31:58
【问题描述】:

鉴于 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.save 发生之前检查所有customer.orders 的有效性。

我知道 TDD 原则是“测试你自己的代码,而不是其他人的”,我希望 Rails 能够完成它的工作。但似乎在没有关闭这个漏洞的情况下,我可能会不小心编写代码来保存孤儿订单并最终在我的数据库中得到无效数据。我不想在检查order.customer 存在时乱扔代码。

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

【问题讨论】:

    标签: ruby-on-rails activerecord


    【解决方案1】:

    我会回滚并更改迁移文件以不允许 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
    

    【讨论】:

    • 有趣。我已经尝试过这个(通常我会阻止 _id 字段上的空值,以便我可以索引它们),但我也有default: 0 - 这只是将问题从阻止空值更改为阻止 0。但你是对的 - 如果你使用不带默认值的null: false,数据库约束将阻止order.save,但customer.save 仍然有效。
    【解决方案2】:

    如果您使用Customer.create 而不是Customer.new,则无需检查order.customer

    【讨论】:

      猜你喜欢
      • 2022-11-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-08-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多