为什么Rails迁移定义应用程序中的外键而不是数据库中?

如果我定义了一个Customer “有很多” OrdersCustomerOrder模型,并且Order “属于” Customer ,我们在Rails中通过customer_id来讨论Order有一个外部关键字,但我们并不是说这个在数据库中执行。

由于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 

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

例如,Rails甚至支持一些不能使用外键的数据库devise,例如Polymorphic Associations。

基本上,Rails约定将数据库视为静态数据存储设备,而不是活动的RDBMS。 Rails 2.0 终于支持一些更现实的SQL数据库function。 结果将是Rails的开发将比1.0版更加复杂,这并不奇怪。

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

应用程序级别的validation和检查可以提供简单,快速,可读的人工(99.99%)的可读性(思考错误消息)检查。 如果您的应用程序需要更多,则应使用数据库级别的限制。

我认为这个“哲学”是由于原来使用的testing框架而演变的:在使用灯具时外键certificate是一个巨大的麻烦。 这就像一个“bug”成为一个“function”,因为没有人修复它。 (如果我误解了历史,有人纠正我)

至less,Rails社区内部正在加强对数据库的完整性的调整。 看看上个月的这篇博文。 她甚至链接到一些插件,以帮助提供处理错误的支持(以及另一篇链接到更多插件的博客文章)。 做更多的Googlesearch; 我见过其他插件,也支持迁移来创build外键。

现在,核心Rails哲学的一部分是:不要担心的东西,除非你真的需要。 对于很多Web应用程序,如果一个很小的(可能很小的)百分比的logging包含无效的数据,那很可能是好的。 可能受到影响的页面可能只能很less被查看,或者错误可以被正常处理。 或者,在应用程序增长的情况下,在未来6个月内手动处理问题比现在更便宜(如同现金一样),而不是花费现在的每个应急开发资源计划。 基本上,如果你的使用案例看起来并不重要,而且只能由可能发生的竞争条件造成1/10000000的要求…呃,值得吗?

所以我的预测是,工具会popup来更好地处理整个情况,最终这些将被合并到Rails 3.同时,如果您的应用程序确实需要它,请添加它们。 这会导致轻微的testing头痛,但是没有什么你不能通过模拟和存根。 如果你的应用程序不需要它,那么你已经很好了。 🙂

经过数十年的行业研究,我坚信一个好的数据库devise将会使应用程序避免许多问题,特别是在进行增强时。 如果知道一个特定的约束条件,即使在一个编程单(我相信我不是唯一能这样做的)之后,也能保持数据库的完整性,如果可能的话,它应该被应用到数据库中。 所以我会鼓励人们尽可能使用外键。 我也会考虑使用testing来提供数据完整性。 因为我们都知道墨菲的法则。

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

对于迁移,我会使用http://github.com/matthuhiggins/foreigner/tree/master 。 你不需要改变你的模型来让外键与Rails一起工作。

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