【问题标题】:Validate that the associated model exists when set in Rails在 Rails 中设置时验证关联模型是否存在
【发布时间】:2023-03-30 04:44:01
【问题描述】:

假设我们在 Rails 中有以下Post 模型:

class Post < ActiveRecord::Base
  belongs_to :user
end

user 不是必需的,但是当它被设置时,我们希望它是一个有效的关联(我们不希望有一个包含不在数据库中的user_id 的帖子)。失败的测试:

expect(Post.new).to be_valid
expect(Post.new(user_id: 0)).not_to be_valid
expect(Post.new(user: User.create)).to be_valid

一种解决方案是:

validates_presence_of :user, unless: "user_id.nil?"

这将完成所需的任务,但不是很漂亮,因为我们不想为我们应用程序中的每个belongs_to 编写它。

有没有办法告诉 Rails 检查关联模型是否存在?这不是我们想要默认拥有的东西吗? (为什么我们想要一个不指向任何东西的association_id?)

【问题讨论】:

标签: ruby-on-rails validation activerecord


【解决方案1】:

为了保证关联在数据库级别有效,MySQL 和 PostgreSQL 能够强制表上的外键列的有效性。 Since Rails 4.2,这些外键约束可以作为迁移的一部分创建。

在迁移过程中,您至少可以通过两种方式做到这一点。将foreign_key 属性传递给add_reference

add_reference :posts, :user, index: true, foreign_key: true

或者用add_foreign_key关注add_reference,这也让你可以选择在删除用户时重置密钥:

add_reference :posts, :user, index: true
add_foreign_key :posts, :user, on_delete: :nullify

通过将哈希传递给add_referenceforeign_key 属性来组合这两种方法(自Rails 4.2.1 起):

add_reference :posts, :user, index: true, foreign_key: {on_delete: :nullify}

如果查询通过更新 user_id 以指向不存在的用户来破坏外键约束,则查询将失败并在 Rails 中引发异常。

正如您所提到的,您还可以在创建 Post 之前创建验证以检查 User 是否存在。但是,这不会像对数据库的外键约束那样有保证,因为在验证通过和创建 Post 之间的时间内可能会删除 User

为了帮助在应用程序级别保持引用有效,通常最好避免将用户 ID 直接传递给 Post 构造函数。相反,请尝试始终使用User 对象作为输入,例如通过调用User.find(:id)

【讨论】:

  • 所以过去 3 天我一直在努力解决这个问题。我将 Rails 4.2.4 与 PostgreSQL 一起使用,但从未进行过约束,尽管添加了此限制:add_reference :users, :school, index: true, foreign_key: { on_update: :restrict, on_delete: :restrict }。根据Rails docs,这应该可行。你知道有什么问题吗?我使用rake db:drop rake db:setup 无济于事。根本就没有做出约束。
  • 这很奇怪,在add_foreign_key 之后使用add_foreign_key 单独设置外键选项会改变什么吗?还可以尝试使用this 之类的内容输出迁移实际生成的SQL 命令,以查看ADD CONSTRAINT 是否出现。
  • ADD CONSTRAINT 根本没有出现。我在 gemfile 和 rails -v 中检查了我的 rails 版本,这两个状态都是 rails 4.2.4。我尝试通过使用add_reference 后跟add_foreign_key 以及设置所有选项的add_reference 方式来添加约束。除此之外,我还尝试使用 belongs_to 和 has_many 设置模型上的关系,但无济于事。不幸的是,我无法找到所有魔法发生的相应 Rails 源代码。那时我可能不得不求助于使用 Foreigner gem。
  • 我终于设法修复它。罪魁祸首是使用rake db:drop | rake db:setup。安装程序从缓存版本加载迁移,该版本没有我编辑的迁移。所以我后来发现我实际上必须做rake db:drop | rake db:create | rake db:migrate。这是因为rake db:create 获取实际的迁移文件并从中构建架构。然后,rake db:migrate 命令正确执行迁移并设置约束。终于!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-02-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多