假设我们在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
?)
答案 0 :(得分: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
通过将哈希传递给foreign_key
的{{1}}属性来结合这两种方法(自Rails 4.2.1起):
add_reference
如果查询通过将add_reference :posts, :user, index: true, foreign_key: {on_delete: :nullify}
更新为指向不存在的用户来中断外键约束,则查询将失败并在Rails中引发异常。
正如您所提到的,您还可以创建验证,以便在创建user_id
之前检查User
是否存在。但是,这不会像数据库上的外键约束那样强大,因为在验证传递和创建Post
之间可以删除User
。
为了帮助在应用程序级别保持引用有效,通常更好的做法是避免将用户ID直接传递给Post
构造函数。相反,请尝试始终使用Post
对象作为输入,例如通过调用User
。