的Rails 3.0.9:ActiveRecord的唯一性约束未能上更新一次,如果唯一的列没有被触及
问题描述:
我有一个剖面模型的Rails 3.0.9:ActiveRecord的唯一性约束未能上更新一次,如果唯一的列没有被触及
class Profile < ActiveRecord::Base
attr_accessible :user_id, :race_id, :nickname, :first_name, :last_name, :gender, :birth_date, :eighteen,
:time_zone, :metric_scale, :referral_code, :referrer_id, :tag_line
# Relationships
belongs_to :user
belongs_to :race
belongs_to :referred_by, :class_name => "Profile", :foreign_key => "referral_code"
has_many :referrals, :class_name => "Profile", :foreign_key => "referrer_id"
# Validations
validates :user_id, :race_id, :nickname, :first_name, :last_name, :time_zone, :gender, :presence => true
validates :referral_code, :nickname, :uniqueness => { :case_sensitive => false }
# Instance Methods
def full_name
first_name + " " + last_name
end
# Class Methods
def self.search(search)
search_condition = "%" + search + "%"
find(:all, :conditions => ['nickname LIKE ?', search_condition])
end
def self.find_by_referral_code(referrer_code)
find(:one, :conditions => ['referral_code LIKE ?', referrer_code])
end
end
无论哪一列我更新了唯一性约束并不重要'referral_code'false,我无法更新模型,我无法弄清楚原因。从我在线阅读的Rails 3 ActiveRecord被认为是跟踪脏对象,并且只生成包含更改的列的更新查询,而只留下所有其他人。因为它应该只对唯一的列以外的列执行更新查询,所以验证不应该失败。不幸的是。这是Rails的控制台会话显示此:
Loading development environment (Rails 3.0.9)
ruby-1.9.2-p180 :001 > profile = Profile.find(3)
=> #<Profile id: 3, user_id: 3, race_id: 2, nickname: "Premium-User", first_name: "Premium", last_name: "User", gender: "M", birth_date: "1977-01-01", eighteen: true, complete: true, time_zone: "Kuala Lumpur", metric_scale: false, referral_code: "bo", referrer_id: nil, tag_line: "This is what its like.", created_at: "2011-09-21 04:08:00", updated_at: "2011-09-21 04:08:00">
ruby-1.9.2-p180 :002 > update = {"tag_line"=>"Changed to this"}
=> {"tag_line"=>"Changed to this"}
ruby-1.9.2-p180 :003 > profile.update_attributes(update)
=> false
ruby-1.9.2-p180 :004 > profile.errors
=> {:referral_code=>["has already been taken"]}
甚至直接对单个列这不是唯一进行的更新会导致唯一性约束失败和记录不会被更新,这里是一个控制台会话:
Loading development environment (Rails 3.0.9)
ruby-1.9.2-p180 :001 > profile = Profile.find(3)
=> #<Profile id: 3, user_id: 3, race_id: 2, nickname: "Premium-User", first_name: "Premium", last_name: "User", gender: "M", birth_date: "1977-01-01", eighteen: true, complete: true, time_zone: "Kuala Lumpur", metric_scale: false, referral_code: "bo", referrer_id: nil, tag_line: "This is what its like.", created_at: "2011-09-21 04:08:00", updated_at: "2011-09-21 04:08:00">
ruby-1.9.2-p180 :002 > profile.tag_line = "changed to this"
=> "changed to this"
ruby-1.9.2-p180 :003 > profile.save
=> false
ruby-1.9.2-p180 :004 > profile.errors
=> {:referral_code=>["has already been taken"]}
我也跑了检查,看是否ActiveRecord的竟是追踪脏对象,这似乎是,这里是控制台会话:
Loading development environment (Rails 3.0.9)
ruby-1.9.2-p180 :001 > profile = Profile.find(3)
=> #<Profile id: 3, user_id: 3, race_id: 2, nickname: "Premium-User", first_name: "Premium", last_name: "User", gender: "M", birth_date: "1977-01-01", eighteen: true, complete: true, time_zone: "Kuala Lumpur", metric_scale: false, referral_code: "bo", referrer_id: nil, tag_line: "This is what its like.", created_at: "2011-09-21 04:08:00", updated_at: "2011-09-21 04:08:00">
ruby-1.9.2-p180 :002 > profile.tag_line = "change to this"
=> "change to this"
ruby-1.9.2-p180 :003 > profile.changes
=> {"tag_line"=>["This is what its like.", "change to this"]}
ruby-1.9.2-p180 :004 > profile.save
=> false
ruby-1.9.2-p180 :005 > profile.errors
=> {:referral_code=>["has already been taken"]}
我说实话我无所畏惧,我花了相当多的时间来挖掘它,并搜索谷歌,我无法找到答案,为什么发生这种情况。
答
您是对的,Rails只会“跟踪”脏列并生成必要的最小更新语句。如果您查看log/development.log文件,您将看到正在生成的实际SQL,并且您会看到更新语句仅触及已编辑的列。至少你会如果你的代码得到这么多。
在保存模型之前,Rails将运行所有验证,包括查看引用代码是否唯一。要做到这一点,它会运行一个选择的SQL语句对数据库进行检查;如果你看看development.log文件,你一定会看到这个查询。
So Rails在这里正常工作。
如果您的推荐代码应该是唯一的,为什么他们不是?我的猜测是你试图用无或空白的代码保存模型。如果是这种情况,请尝试将:allow_nil => true
或:allow_blank => true
添加到唯一性散列。
你从Profile.find_all_by_referral_code(profile.referral_code)得到什么? – klochner
这个约束是否也在数据库级别实现?转储该表的DDL并检查是否有任何键/约束。同时检查log/development.log以获取更新正在运行的实际SQL查询。 –