【问题标题】:validates_presence_of with belongs_to associations, the right wayvalidates_presence_of 与 belongs_to 关联,正确的方式
【发布时间】:2011-05-27 16:52:38
【问题描述】:

我正在研究 validates_presence_of 的实际工作原理。假设我有两个模型

class Project < ActiveRecord::Base
  [...]
  has_many :roles
end

class Role < ActiveRecord::Base
  validates_presence_of :name, :project

  belongs_to :project
end

我希望角色始终属于现有项目,但我刚刚从this example 发现这可能导致保存到数据库中的无效(孤立)角色。所以正确的做法是在我的角色模型中插入validates_presence_of :project_id,它似乎可以工作,即使我认为在语义上验证项目的存在而不是项目 ID 更有意义。

此外,我认为如果我只是验证 project_id 的存在,我可以放置一个无效的 id(对于一个不存在的项目),因为默认情况下 AR 不会向迁移添加完整性检查,即使我添加了它们手动一些数据库不支持它们(即带有 MyISAM 或 sqlite 的 MySQL)。这个例子证明了

# with validates_presence_of :name, :project, :project_id in the role class
Role.create!(:name => 'foo', :project_id => 1334, :project => Project.new)
  AREL (0.4ms)  INSERT INTO "roles" ("name", "project_id") VALUES ('foo', NULL)
+----+------+------------+
| id | name | project_id |
+----+------+------------+
| 7  | foo  |            |
+----+------+------------+

我当然不会写这样的代码,但是我想防止DB中出现这种错误数据。

我想知道如何确保角色始终与(真实且已保存的)项目相关联。

我找到了validates_existence gem,但除非绝对必要,否则我宁愿不将 gem 添加到我的项目中。

对此有什么想法吗?

更新

validates_presence_of :project 并在迁移中为 project_id 列添加:null =&gt; false 似乎是一种更清洁的解决方案。

【问题讨论】:

  • 我强烈建议为此使用 validates_existence gem,因为它正是您所需要的。此外,它是一个相当小的依赖项。
  • 只是一个快速的不 - 确保您也使用您的数据库进行验证。让生活更安全。
  • @Jits,我想我会这样做的。 @Chuck 我也会这样做,但这样我就不会出现验证错误,所以我仍然需要在 ruby​​ 级别进行验证。

标签: ruby-on-rails validation activerecord


【解决方案1】:

Rails 将尝试在 id 上查找,如果找不到具有 id 的对象,则会添加验证错误。

class Role < AR::Base
  belongs_to :project
  validates_presence_of :project, :name
end


Role.create!(:name => "admin", :project_id => 1334)# Project 1334 does not exist
# => validation error raised

我看到您的问题还想处理提供了作者对象但是新对象而不在数据库中的情况。如果存在检查不起作用。会解决的。

Role.create!(:name => "admin", :project => Project.new) # Validation passes when it shouldn't.

更新: 在某种程度上,您可以通过对关联的 :project 进行验证来减轻传递虚拟新对象的影响。

class Role < ActiveRecord::Base
  belongs_to :project
  validates_presence_of :project
  validates_associated :project
end

如果Project.new.valid? 为假,那么Role.create!(:name =&gt; "admin", :project =&gt; Project.new) 也会引发错误。但是,如果Project.new.valid? 为真,则上述内容将在保存时创建一个项目对象。

使用validates_associated :project 对您有帮助吗?

【讨论】:

  • 我不喜欢validates_associated :project,因为我似乎需要两条线来满足相同的要求,即必须存在一个项目,但它确实有效。
【解决方案2】:

我尝试了很多验证器组合,但最简洁的解决方案是使用 validates_existence gem。有了它,我可以编写这样的代码

r = Role.new(:name => 'foo', :project => Project.new) # => #<Role id: nil, name: "foo", project_id: nil, created_at: nil, updated_at: nil> 
r.valid? # => false 
r.errors # => {:project=>["does not exist"], :project_id=>["does not exist"]} 

所以我的最终模型很简单

class Role < ActiveRecord::Base
  belongs_to :project
  validates_existence_of :project
  # or with alternate syntax
  validates :project, :existence => true
  [...]
end

使用 db 验证和 Aditya 解决方案(即迁移中的 :null => false 和模型中的 validates_presence_of :project)Role#valid? 将返回 true,Role#save 将在 project_id 为空时在数据库级别引发异常。

【讨论】:

    猜你喜欢
    • 2015-02-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多