使用<<<<<<<<<<<<<<<<<语法

时间:2016-02-02 04:11:53

标签: ruby-on-rails ruby ruby-on-rails-4 activerecord rails-activerecord

我在一个简单的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

1 个答案:

答案 0 :(得分:1)

这一行:Profile.first.majors << Major.create(:name => 'basketweaving')

...分两步执行:

  1. Major.create(:name => 'basketweaving')
  2. 这将创建一个新的Major对象,但它尚未与配置文件关联。因此,profile_id为零。

    1. Profile.first.majors << (newly created Major object)
    2. 这将获取新创建的对象并尝试将其添加到第一个Profile的主要集合中。这样做是通过更改Major对象的profile_id来匹配Profile对象的。{/ p>

      第一次运行时,步骤1和2都成功。但这是第二次发生的事情:

      1. Major对象是使用nil profile_id
      2. 创建的
      3. 由于唯一性验证,它无法将Major对象添加到集合中。已有一个Major对象具有相同的名称和profile_id。虽然没有回滚通知,但它确实返回false而不是返回集合。因此,Major对象的profile_id仍为nil
      4. ......第三次:

        1. 由于唯一性验证,未创建Major对象。该名称已有Major个对象,profile_idnil
        2. <强>建议

          由于这个怪癖,我建议像这样添加到集合中:Profile.first.majors.create(:name => 'basketweaving')。这样,你就可以在集合本身上调用一个方法。

          此外,如果您想确保Major对象永远不会有nil profile_id,请在Major上设置状态验证:validates :profile, presence: true < / p>