validates_presence_of与belongs_to关联,正确的方式

我正在调查validates_presence_of实际上是如何工作的。 假设我有两个模型

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

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

我想要它,以便角色总是属于现有项目,但我只是从这个例子中发现,这可能导致无效(孤立)角色保存到数据库中。 因此,正确的方法是在我的Role模型中插入validates_presence_of :project_id ,它似乎有效,即使我认为在语义上更有意义来validation项目的存在而不是项目ID。

除此之外,如果我只是validationproject_id的存在,我认为我可以放置一个无效的id(对于一个非现有的项目),因为默认情况下AR不会为迁移添加完整性检查,即使我手动添加了一些DB不支持它们(即MySQL与MyISAM或sqlite)。 这个例子certificate了

 # 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 => false似乎是一个更清晰的解决方案。

如果找不到具有id的对象,Rails将尝试查找id并添加validation错误。

 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 

我看到你的问题也想处理提供作者对象但是新的而不是db的情况。 如果存在检查不起作用。 会解决。

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

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

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

如果Project.new.valid? 是false,然后Role.create!(:name => "admin", :project => Project.new)也会引发错误。 但是,如果Project.new.valid? 如果是,则上面将在保存时创建项目对象。

使用validates_associated :project帮助你吗?

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

 r = Role.new(:name => 'foo', :project => Project.new) # => # 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 

使用dbvalidation和Aditya解决方案(即:在迁移中为null => false,在模型中为validates_presence_of:项目) Role#valid? 将返回true,当project_id为null时, Role#save将在数据库级别引发exception。