我看到一个在Rails中同时具有数据库级和代码级验证的表。
CREATE UNIQUE INDEX some_index_name ON joined_table_name USING btree (user_id, restriction_id);
和代码级别:
class SomeJoinTable
validates_uniqueness_of :restriction_id, :scope => :user_id
end
同时拥有两者的好处是什么?
答案 0 :(得分:6)
引用excellent article by thoughtbot:
当您持久保存用户实例时,Rails将通过运行SELECT查询来验证您的模型,以查看所提供的电子邮件是否已存在任何用户记录。假设记录证明有效,Rails将运行INSERT语句以持久保存用户。这在开发中非常有用,如果您运行单个进程Web服务器的单个实例,甚至可以在生产中工作。
但是你没有运行WEBrick的单独实例,是吗?不,为了最大化每分钟的请求,您在多个Heroku dynos上运行Unicorn,每个都有多个Web进程。让我们来看看如果只有两个进程试图在同一时间创建具有相同电子邮件地址的用户会发生什么:
...您可以在使用以下内容生成迁移或模型时创建唯一索引:
rails generate model user email:string:uniq
有了索引,上面的场景现在如何发挥作用?
现在我们有数据库作为我们对不一致数据的战争的最后一道防线。第二个保存操作将生成ActiveRecord :: RecordNotUnique异常。
结论
Rails做了很多事情,但数据完整性验证不是其中之一。您的关系数据库旨在强制数据完整性;让它。
修改强>
添加了除了db约束之外你还要使用validates_uniqueness_of
的原因示例(根据OP的评论):
我认为它解释了为什么需要数据库级验证,但我的问题也是问为什么我们不能进行数据库级验证。
<强> ANSWER 强>
进一步引用thoughtbot's article:
第二个保存操作将生成ActiveRecord :: RecordNotUnique异常。在大多数情况下,这将导致应用程序错误。如果您需要提供更好的体验,可以在控制器操作中挽救和处理该异常,或者在类级别使用rescue_from。
让我们在课堂上看到这一点。
仅限数据库级约束:
class Foo < ApplicationRecord
belongs_to :bar
validates_presence_of :name
end
class Bar < ApplicationRecord
has_many :foos
end
> Foo.create!(bar_id: 1, name: 'Poop')
> Foo.create!(bar_id: 1, name: 'Poop')
ActiveRecord::RecordNotUnique: PG::UniqueViolation: ERROR: duplicate key value violates unique constraint "index_foos_on..."
使用数据库约束和应用程序级验证(la validates_uniqueness_of
):
class Foo < ApplicationRecord
belongs_to :bar
validates_presence_of :name
validates_uniqueness_of :name, scope: :bar_id
end
> Foo.create!(bar_id: 1, name: 'Poop')
> Foo.create!(bar_id: 1, name: 'Poop')
ActiveRecord::RecordInvalid: Validation failed: Name has already been taken
答案 1 :(得分:0)
同时拥有两者有什么好处?
除了显而易见的“额外安全性”方面,让validates_uniqueness_of
在您的应用程序端添加质量门,以便在尝试提交之前进行检查。
如果没有validates_uniqueness_of ,它将尝试查询要创建的数据库,因此错误将从数据库返回。
使用validates_uniqueness_of ,它将在尝试创建之前查询数据库以查看它是否存在,因此错误将从应用返回。由于您可以完全控制,因此最好从应用程序返回错误。
还要考虑一下...... 我可能会在以后看到问题,你从数据库中删除了这个约束,但是等等..你的模型仍然有这个约束!或相反亦然。
答案 2 :(得分:0)
同时拥有两者的好处是什么?
db级别的防护是最终的保护。这是保持数据一致的原因。
应用程序级别的警卫虽然也做了一些检查,但不能依赖它来保持数据的一致性。其目的是支持ActiveRecord验证(填充错误数组,以便显示漂亮的表单错误等)。
因此,如果您只有数据库防护,则必须做额外的工作来拦截数据库错误并显示用户友好的表单错误。
如果你只有应用程序级别的检查,你也可以删除它。它很容易受到竞争条件的影响,迟早会发生 违规行为。