validates_presence_of与belongs_to关联,正确的方式
我正在调查validates_presence_of实际如何工作。假设我有两个型号validates_presence_of与belongs_to关联,正确的方式
class Project < ActiveRecord::Base
[...]
has_many :roles
end
和
class Role < ActiveRecord::Base
validates_presence_of :name, :project
belongs_to :project
end
我希望它让角色始终属于现有的项目,但我刚刚从this example发现,这可能会导致保存无效(孤立的)角色进入数据库。所以正确的做法是在我的Role模型中插入validates_presence_of :project_id
,它似乎工作,即使我认为语义上有更多的意义来验证项目的存在而不是项目ID。
除此之外,我想我可以把一个无效的id(对于一个不存在的项目),如果我只是验证project_id的存在,因为默认情况下AR不会为迁移添加完整性检查,即使我添加他们手动某些数据库不支持他们(即MySQL与MyISAM或sqlite)。这个例子证明
# 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宝石,但我宁愿不添加宝石到我的项目中,除非是绝对必要的。
对此有何想法?
更新
validates_presence_of :project
和在迁移PROJECT_ID列添加:null => false
似乎是一个清洁的解决方案。
我尝试了很多验证器的组合,但最干净的解决方案是使用validates_existence宝石。有了,我可以写这样
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验证加阿迪亚溶液(即:空=>在迁移和validates_presence_of假:项目在模型中)Role#valid?
将返回true,Role#save
将在project_id为空时在数据库级别引发异常。
如果找不到具有id的对象,Rails将尝试在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
我看到你的问题也想处理作者对象提供的情况,但是新的而不是db。在存在检查不起作用的情况下。将解决。
Role.create!(:name => "admin", :project => Project.new) # Validation passes when it shouldn't.
更新: 在一定程度上可以减轻这样做对相关的验证通过虚设新对象的效果:项目。
class Role < ActiveRecord::Base
belongs_to :project
validates_presence_of :project
validates_associated :project
end
如果Project.new.valid?
是假的,然后Role.create!(:name => "admin", :project => Project.new)
也将产生一个错误。但是,如果Project.new.valid?
为真,那么上述内容将在保存时创建一个项目对象。
是否使用validates_associated :project
对您有帮助?
我不喜欢'validates_associated:project',因为它似乎需要两条线才能满足相同的要求,即项目必须存在,但它确实有效。 – Fabio 2011-06-01 02:08:19
我强烈建议使用validates_existence gem来做这件事,因为它正是你需要的。此外,它具有相当小的依赖性。 – Jits 2011-06-02 13:47:50
只是一个快速不 - 确保你使用你的数据库来验证。让生活更安全。 – CharlesJHardy 2011-06-02 17:34:20
@Jits,我想我会那样做的。 @Chuck我也会这样做,但这样我就不会有验证错误,所以我仍然需要在ruby级别进行验证。 – Fabio 2011-06-06 16:57:21