【问题标题】:Why do Rails migrations define foreign keys in the application but not in the database?为什么 Rails 迁移在应用程序中定义外键而不是在数据库中?
【发布时间】:2009-05-29 22:30:54
【问题描述】:

如果我定义一个 CustomerOrder 模型,其中 Customer “有很多” OrdersOrder “属于” Customer,在 Rails 中我们谈论 Order具有Customercustomer_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

【问题讨论】:

    标签: ruby-on-rails database foreign-keys referential-integrity


    【解决方案1】:

    Rails 拥有一些约定,即应在应用程序中而不是在数据库中执行数据完整性。

    例如,Rails 甚至支持cannot use foreign keys 的一些数据库设计,例如多态关联。

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

    【讨论】:

    • 您指的是 Rails 2.0 的哪些特性?
    • 我正在考虑的一个功能是支持未命名为“id”的主键列。我不是普通的 Rails 开发人员,所以我不会跟踪他们的所有新功能。
    • 好的。总的来说,为了纠正 Rails 中的这种设计,您是否建议定义数据库级外键来加强应用程序级外键?或者这是一个真正取决于应用程序的判断调用?我的印象是,在上面的示例中,总是建议定义数据库级外键。
    • 我同意在数据库中定义约束是最佳实践。只有这样,您才能确保始终如一地执行数据完整性。如果您在应用程序中执行此操作,那么任何不通过您的应用程序访问数据库的人都不会受到相同的约束,他们会弄乱数据。但是,我不知道如何说服 Rails 迁移实现真正的数据库约束。这是我不使用 Rails 的原因之一。
    • 当然。不过,我指的是在运行 Rails 迁移之后在数据库中手动定义约束的选项。不幸的结果是,并非所有 DDL 都应用在一个地方。
    【解决方案2】:

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

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

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

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

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

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

    【讨论】:

    • 这看起来很现实、合理、实用。很棒的博文(“她”不是“他”顺便说一句)。感谢您的链接。我要试试那个插件。
    【解决方案3】:

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

    【讨论】:

    • 感谢您分享您的经验,非常感谢,先生。
    【解决方案4】:

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

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

    【讨论】:

      【解决方案5】:

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

      【讨论】:

      • "Rails 开发人员的观点是这样的约束应该由应用程序来处理,而不是由数据库来处理"但是他们为什么会这样呢?这不是数据库最擅长的吗?
      • 我想这很大程度上与便携性有关。 Rails 支持许多 DB 后端,但并非所有后端都必须支持相同的功能集。但是,Rails 试图抽象出 DB,因此在许多情况下选择仅支持最低公分母。我想也可能有其他原因; Rails 核心开发人员都是狂热的博主,所以我想如果您阅读他们的博客,您可能会找到更清晰的答案。
      • 可以在所有主要数据库中定义参考约束。你的解释没有道理。
      • @eggdrop:SQLite 不支持外键约束。 MySQL 的默认存储引擎 (MyISAM) 不支持外键约束。这两个 SQL 数据库非常流行,尤其是在开源部署中。
      • 没有人将 SQLite 用于生产 Web 应用程序。与 InnoDB 相比,MyISAM 的使用率很低,因为 MyISAM 也不支持事务。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-03-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-11-23
      • 1970-01-01
      相关资源
      最近更新 更多