35

如果我定义一个模型,其中一个Customer“有很多”和“属于”的,在 Rails 中我们谈论有一个外键,但我们并不是说这在数据库中是强制的。OrderCustomerOrdersOrderCustomerOrderCustomercustomer_id

因为 Rails 没有将此定义为数据库级别的约束,所以可能存在违反数据完整性的风险,可能在应用程序外部(或者如果您同时收到请求,则在内部?),除非您在数据库中手动强制执行该约束。

为什么 Rails 不在数据库级别定义外键,或者有没有办法让 Rails 做到这一点?

class Customer < ActiveRecord::Base
  has_many :orders
end

class Order < ActiveRecord::Base
    belongs_to :customer
end

ActiveRecord::Schema.define(:version => 1) do

  create_table "customers", :force => true do |t|
    t.string   "name"
  end

  create_table "orders", :force => true do |t|
    t.string   "item_name"
    t.integer  "customer_id"
  end

end
4

5 回答 5

22

Rails 持有一些约定,即数据完整性的实施应该在应用程序中完成,而不是在数据库中。

例如,Rails 甚至支持一些不能使用外键的数据库设计,例如多态关联。

基本上,Rails 约定将数据库视为静态数据存储设备,而不是活动的 RDBMS。Rails 2.0终于支持 SQL 数据库的一些更现实的特性。毫无疑问,结果是使用 Rails 进行开发将变得比 1.0 版更复杂。

于 2009-05-29T22:40:34.080 回答
13

在处理了这个问题一段时间之后,我不认为数据库不应该强制执行外键是 Rails 核心理念的一部分。

应用程序级别的验证和检查可提供简单、快速、人类可读(想想错误消息)的检查,这些检查在 99.99% 的时间内都有效。如果您的应用程序需要更多,您应该使用数据库级别的约束。

我认为这种“哲学”是由于使用了原始测试框架而演变而来的:外键在使用固定装置时被证明是一个巨大的麻烦。这就像一个“bug”变成了一个“特性”,因为没有人修复它。(如果我记错了历史,请有人纠正我。)

至少,Rails 社区内有越来越多的运动来加强数据库的完整性。 查看上个月的这篇博文。 她甚至链接到一些有助于为处理错误提供支持的插件(以及另一个链接到更多插件的博客文章)。再做几次谷歌搜索;我也看到了其他插件支持迁移以创建外键。

现在,核心 Rails 哲学一部分是:除非您确实需要,否则不要担心任何事情。对于许多 Web 应用程序,如果一小部分(可能很小)的记录包含无效数据,这可能是可以的。可能受到影响的页面可能很少被查看,或者错误已经可以很好地处理。或者,随着应用程序的增长,在接下来的 6 个月内手动处理问题可能比现在花费开发资源规划每个突发事件更便宜(例如,冷硬现金)。基本上,如果您的用例并没有使它看起来很重要,并且它实际上只能由可能发生 1/10000000 个请求的竞争条件引起……那么,值得吗?

所以我的预测是,默认情况下会出现工具来更好地处理整个情况,最终这些工具将被合并到 Rails 3 中。与此同时,如果您的应用程序确实需要它,请添加它们。它会引起轻微的测试头痛,但没有什么是你无法通过模拟和存根完成的。如果您的应用程序并不真正需要它……那么您已经很好了。:)

于 2009-05-30T01:15:05.843 回答
6

经过几十年的行业工作,我坚信一个好的数据库设计将使应用程序免于许多问题,尤其是在它经历增强时。如果已知某个特定的约束即使在编程失误之后也能保持数据库的完整性(我相信我不是唯一这样做的人),那么如果可能的话,应该将它应用到数据库中。所以我会鼓励人们尽可能使用外键。我还会考虑使用测试来提供数据完整性。因为我们都知道墨菲定律。

于 2011-03-23T17:32:19.763 回答
4

许多人犯的一个错误是将迁移与模型混淆。迁移只是修改数据库,与您定义的模型无关。由于这种混乱,许多外键插件尝试将模型与迁移结合起来,做太多神奇的事情。

对于迁移,我会使用http://github.com/matthuhiggins/foreigner/tree/master。您无需更改模型即可让外键与 Rails 一起使用。

于 2009-09-04T00:31:52.777 回答
2

它确实创建了一个customer_id列(显然)。不过,在大多数情况下,Rails 相信在应用程序级别而不是数据库级别强制执行约束和验证。这就是为什么默认情况下,Rails 中的列可能包含NULL值,即使你有validates_presence_of或类似的东西。Rails 开发人员的观点是,此类约束应该由应用程序处理,而不是由数据库处理。

于 2009-05-29T22:37:23.057 回答