Jwa*_*622 2 validation ruby-on-rails unique-index
我看到一个在Rails中同时具有db级和代码级验证的表.
CREATE UNIQUE INDEX some_index_name ON joined_table_name USING btree (user_id, restriction_id);
Run Code Online (Sandbox Code Playgroud)
和代码级别:
class SomeJoinTable
validates_uniqueness_of :restriction_id, :scope => :user_id
end
Run Code Online (Sandbox Code Playgroud)
两者兼有的好处是什么?
当您持久保存用户实例时,Rails将通过运行SELECT查询来验证您的模型,以查看是否已存在所提供的电子邮件的任何用户记录.假设记录证明有效,Rails将运行INSERT语句以持久保存用户.这在开发中非常有用,如果您运行单个进程Web服务器的单个实例,它甚至可以在生产中工作.
但是你没有运行WEBrick的单独实例,是吗?不,为了最大化每分钟的请求,您在多个Heroku dynos上运行Unicorn,每个都有多个Web进程.让我们来看看如果其中两个进程试图在同一时间创建具有相同电子邮件地址的用户会发生什么:
...您可以在生成迁移或模型时创建唯一索引:
rails generate model user email:string:uniq
随着指数的到位,上述情景现在如何发挥作用?
现在我们将数据库作为我们对不一致数据的战争的最后一道防线.第二个保存操作将生成ActiveRecord :: RecordNotUnique异常.
结论
Rails做了很多事情,但数据完整性验证不是其中之一.您的关系数据库旨在强制数据完整性; 让它.
编辑
validates_uniqueness_of除了db约束之外,还添加了为什么要使用的示例(根据OP的注释):
我认为它解释了为什么需要数据库级验证,但我的问题也是问为什么我们不能只进行数据库级验证.
回答
进一步引用thinkbot的文章:
第二个保存操作将生成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..."
Run Code Online (Sandbox Code Playgroud)
使用数据库约束和应用程序级验证(a 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
Run Code Online (Sandbox Code Playgroud)