为什么你应该停止在 Ruby on Rails 中使用 ActiveModel 验证
为什么你应该停止在 Ruby on Rails 中使用 ActiveModel 验证
关于切换好处的深入指南
image from imgflip
Ruby on Rails 在很多事情上都很棒,而且正如你们中的许多人所知,模型中的验证很酷,因为它们简单、实现快速且易于测试。
但他们也很烂。
注意:本文仅适用于您使用 PostgreSQL 的情况。如果您使用的是 SQLite 或 MySQL,请停止。
这些是什么?
验证是类似的方法[ 验证](https://guides.rubyonrails.org/active_record_validations.html#validations-overview)
,[ validates_related](https://guides.rubyonrails.org/active_record_validations.html#validates-associated)
等等,它将根据某些逻辑(可以是唯一性、字符串的格式或您自己的自定义逻辑)检查实例,并允许或禁止将其插入数据库。
使用模型验证有很多缺点。让我们一起来看看吧!
它只在 Rails 层可行
假设您的数据库在多个团队之间共享。您的数据团队直接插入您的主数据库,或者其他系统可以与数据库交互而无需通过您的 API。因此,您的验证和回调将被绕过,您可以在数据库中拥有对您的 RDBMS(例如 Postgresql)有效但对您的 rails 应用程序无效的数据。它可以“默默地”瘫痪你的系统。
实施后更改验证规则是不安全的
如果开发人员添加或更改任何验证规则,则可能会在新旧数据库规则之间产生差异。以前认为有效的记录现在将被视为无效而没有任何警告!这可能会使整个数
“好吧,我知道这可能很麻烦。你建议我们做什么来解决这个问题?”
PostgreSQL 的限制,当然!
一个基本的例子——让我们把手弄脏!
这些是什么?
您应用于数据库模式以插入值(如验证)的“规则”。
让我们看一个快速示例,说明如何将简单的验证规则转换为约束。
-
让我们建立一个快速项目来使用这段代码:
rails new constraint_example --database=postgresql
-
cd进去:
cd 约束示例
-
对于这个例子,我们需要一个模型。让我们称之为
用户
(我知道,多么原始)。bin/rails 生成模型用户电子邮件:字符串年龄:整数
上面的命令应该创建一个如下所示的迁移文件:
现在启动 bin/rails 数据库:创建数据库:迁移
.这将从迁移中创建 db 和 user 表。
让我们来看看 User 模型吧!
默认情况下,它应该几乎是空的:
类用户 < 申请记录
结尾
我们需要改变它,所以我们所有的用户都有独特的电子邮件。使用 rails 验证,它看起来像这样:
类用户 < 申请记录
验证:电子邮件,唯一性:真
结尾
这将告诉 Rails 检查所有电子邮件 用户
表并在创建或更新之前查看它是否已经存在 用户
记录。
让我们创建我们的第一个 用户
记录!
1.启动一个rails控制台: 垃圾箱/导轨 c
2.创建一个简单的用户: User.create(电子邮件:'[[email protected]](/cdn-cgi/l/email-protection)',年龄:42)
3. 看看 rails 控制台日志告诉你什么。我们应该有如下所示的日志:
看到第二行了吗?由于我们的验证规则,Rails 将执行查询以检查数据库中是否存在记录 应用程序/模型/user.rb
.
Rails 看到没有返回记录,所以它创建一个并返回新创建的实例!太好了,我们可以说。
当然,这么少的代码就能得到这样的结果真是太棒了!那么有什么问题呢?
我们每次在没有索引的列上创建或更新新用户时都会启动一个查询。如果您的数据库可扩展并容纳数百万 用户
记录,这可能导致 插入
比他们需要的要慢得多,因为他们总是会在前面加上一个 选择
在没有索引的列上。这是第一个问题。
第二个问题是,只有当您尝试通过您的 rails 应用程序创建/更新记录时,才会执行此查询。如果您有其他应用程序会在同一数据库上创建数据,它们将忽略 Rails 验证,因为它们不知道这一点。假设我们直接从数据库中使用同一电子邮件创建第二条记录:
-
访问您的数据库:
psql -d 约束示例开发
-
创建另一个记录:
插入用户(id、电子邮件、年龄、created_at、updated_at)值(2,' 简单@example.com ', 25, '2022-09-10 11:19:20.755912', '2022-09-10 11:19:20.755912');
由于它在数据库级别,Postgres 绕过 Rails 层并跳过 Rails 验证。第二条记录创建成功。
3. 现在,回到 Rails 控制台: 垃圾箱/导轨 c
4. 更新第一个用户的年龄! User.first.update!(年龄:53)
看最后一行:
/Users/yorick/.rvm/gems/ruby-2.7.4/gems/activerecord-7.0.3.1/lib/active_record/validations.rb:80:in `raise_validation_error':验证失败:电子邮件已被使用(ActiveRecord: :记录无效)
我们有一个错误告诉我们电子邮件无效,但我们没有更改它的电子邮件!
Rails 不在乎。
如果只有 rails 拥有数据库的密钥,并且您从不打算通过 rails 以外的其他方式操作数据,那么 rails 验证是一种“可接受的”方式。
让我们尝试不同的方法——使用 PostgreSQL 的约束和索引!
- 让我们创建一个迁移:
bin/rails g 迁移 AddEmailConstraintToUsers
- 对于这样一个简单的情况,我们可以使用 rails 方式:
我知道,我一直在谈论约束,但我们为什么要使用 添加索引
这里?创建一个 unique_constraint
与创建一个相同 唯一索引
.基于 PostgreSQL 官方文档 :
当为表定义唯一约束或主键时,PostgreSQL 会自动创建唯一索引。索引涵盖构成主键或唯一约束的列(多列索引,如果适用),并且是强制执行约束的机制。
所以创建唯一约束与创建唯一索引是一样的。
还记得 Rails 过去是如何在我们每次想要创建或更新记录时进行查询的吗? Postgres 现在会自己做同样的事情,但速度更快,这要归功于索引。
好的,现在让我们运行迁移!
bin/rails 数据库:迁移
看起来我们有一个错误,如下所示:
造成的:
PG::UniqueViolation:错误:无法创建唯一索引“index_users_on_email”
详细信息:密钥(电子邮件)=([ 简单@example.com](/cdn-cgi/l/email-protection#bac9d3d7cad6dffadfc2dbd7cad6df94d9d5d7) ) 重复。
/Users/yorick/Work/constraint_ecample/db/migrate/20220910115344_add_email_constraint_to_users.rb:3:in `change'
任务:TOP => db:migrate
(通过使用 --trace 运行任务查看完整跟踪)
作为 文档 说:
当索引被声明为唯一时,不允许具有相同索引值的多个表行
这意味着我们必须在添加该约束之前进行清理。这是您将来希望迁移到 PG 约束时必须考虑的事情
让我们使用以下代码进行清理:
现在,启动迁移 bin/rails 数据库:迁移
.
让我们回到我们的 应用程序/模型/user.rb
并删除我们添加的验证:
类用户 < ApplicationRecord 结尾
现在,让我们回到 Rails 控制台并添加以下代码:
The SELECT before Create has disappeared.
Rails 不再独立进行查询,因为它不再意识到这个约束。该工作留给 Postgres 来验证记录的创建。
只是为了它,让我们尝试验证我们不能使用相同的电子邮件创建第二个用户。这是代码:
It still raises ActiveRecord::RecordNotUnique because ActiveRecord will catch PG’s PG::UniqueViolation
现在,让我们尝试在 PostgreSQL 级别创建一个副本。
-
启动 psql 控制台:
psql -d test_app_development
2.尝试创建重复记录:
插入用户(id、电子邮件、年龄、created_at、updated_at)值(1,'[ 简单@example.com](/cdn-cgi/l/email-protection#f98a909489959cb99c81989489959cd79a9694) ', 42, '2022-09-10 11:19:20.755912', '2022-09-10 11:19:20.755912');
错误:重复键值违反唯一约束“index_users_on_email”
详细信息:密钥(电子邮件)=([ 简单@example.com](/cdn-cgi/l/email-protection#4a3923273a262f0a2f322b273a262f64292527) ) 已经存在。
有用!您已经创建了一个比 Rails 验证更快、更安全的简单约束!
使用 PG 约束的缺点——以及如何应对它们
我在本文开头提到了 Rails 验证的缺点。现在让我们尝试对 PostgreSQL 约束做同样的事情。
它混淆了数据模型的一些关键逻辑概念
要了解模型的约束,您必须深入研究 架构.rb
看看它是否有任何验证。对于大型应用程序,有一个正常的 架构.rb
文件是一千行长并且不是很容易理解的文件。
为了解决这个问题,我建议添加 awesome[ 注释](https://github.com/ctran/annotate_models)
gem 到您的 Gemfile 中 发展
团体:
组:开发做 ...
宝石'注释'
...
结尾
然后,运行 捆绑安装
, 其次是
bin/rails g 注释:安装
最后,运行 捆绑执行注释
, 如下所示:
annotate has notes on models, which is very useful!
这 注释
gem 将跟踪对表的更改,并在表模型的顶部打印其列、索引和约束的列表。
更改约束需要迁移
由于它在您的数据库模式中,因此您不能再通过简单的提交来更改规则,仅此而已。您必须创建一个迁移;如果某些数据不符合约束,迁移将失败。有人会认为这是使用 PG 约束的一个陷阱,但我会说它更好。
本文以唯一约束为例,但可以添加其他约束,如检查约束、NOT NULL 约束等。可以在官方了解更多 关于约束的 PostgreSQL 文档 .基本上,使用 ActiveRecord Validation 可以完成的所有事情都可以通过 PostgreSQL 约束来完成!
接下来 : 为什么以及如何摆脱 活动记录
Ruby on Rails 中的回调!敬请期待。
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明