我在一个简单的has_many关联上遇到了一个与validates_uniqueness_of有关的奇怪问题。以下是模型的简化版本:
#profile.rb
class Profile < ActiveRecord::Base
...
has_many :majors, -> { uniq }, :dependent => :destroy
...
end
#major.rb
class Major < ActiveRecord::Base
belongs_to :profile
validates_uniqueness_of :name, :scope => :profile
...
end
当我从rails控制台添加关联时,会出现奇怪的行为:
> Profile.first.majors << Major.create(:name => 'basketweaving')
第一个按预期添加。然后我又跑了......
> Profile.first.majors << Major.create(:name => 'basketweaving')
第二个返回成功的结果,没有回滚或唯一性验证错误。我期望这个插入的错误和回滚,我留下了如下所示的记录。请注意,第二条记录中有nil profile_id但由于某种原因它仍然被保存。
[
[0] #<Major:0x007ff72bdbfce8> {
:id => 20,
:name => "Finance",
:profile_id => 5,
:school_code => nil,
:created_at => Tue, 02 Feb 2016 03:48:56 UTC +00:00,
:updated_at => Tue, 02 Feb 2016 03:48:56 UTC +00:00
},
[1] #<Major:0x007ff72bdbfba8> {
:id => 21,
:name => "Finance",
:profile_id => nil,
:school_code => nil,
:created_at => Tue, 02 Feb 2016 03:48:58 UTC +00:00,
:updated_at => Tue, 02 Feb 2016 03:48:58 UTC +00:00
}
]
我第三次运行命令:
> Profile.first.majors << Major.create(:name => 'basketweaving')
我得到了我期望的回滚,但它似乎正在回滚,因为Major with:name =&gt; 'basketweaving'和nil profile_id是重复的。这似乎并不直观,我最终得到了那些孤立的记录。有一个更好的方法吗?我应该做点什么:
unless Profile.first.majors.where(:name => 'basketweaving').exists?
Profile.first.majors << Major.create(:name => 'basketweaving')
end
答案 0 :(得分:1)
这一行:Profile.first.majors << Major.create(:name => 'basketweaving')
...分两步执行:
Major.create(:name => 'basketweaving')
这将创建一个新的Major
对象,但它尚未与配置文件关联。因此,profile_id
为零。
Profile.first.majors << (newly created Major object)
这将获取新创建的对象并尝试将其添加到第一个Profile
的主要集合中。这样做是通过更改Major
对象的profile_id
来匹配Profile
对象的。{/ p>
第一次运行时,步骤1和2都成功。但这是第二次发生的事情:
Major
对象是使用nil
profile_id
Major
对象添加到集合中。已有一个Major
对象具有相同的名称和profile_id
。虽然没有回滚通知,但它确实返回false
而不是返回集合。因此,Major
对象的profile_id
仍为nil
。......第三次:
Major
对象。该名称已有Major
个对象,profile_id
为nil
。<强>建议强>
由于这个怪癖,我建议像这样添加到集合中:Profile.first.majors.create(:name => 'basketweaving')
。这样,你就可以在集合本身上调用一个方法。
此外,如果您想确保Major
对象永远不会有nil
profile_id
,请在Major
上设置状态验证:validates :profile, presence: true
< / p>