Rails 3.0.9: ограничение уникальности ActiveRecord не выполняется при каждом обновлении, не имеет значения, не затронут ли уникальный столбец - PullRequest
0 голосов
/ 30 сентября 2011

У меня есть модель профиля

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 Console, отображающий это:

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"]}

Честно говоря, я в растерянности, я потратил немало времени, копаясь в нем, а также в поиске в Google, и я не могу найти ответ, почему это происходит.

1 Ответ

0 голосов
/ 01 октября 2011

Вы правы, Rails только «отслеживает» грязные столбцы и генерирует минимальный необходимый оператор обновления. Если вы заглянете в файл log / development.log, то увидите фактический генерируемый SQL и увидите, что оператор обновления касается только тех столбцов, которые вы отредактировали. По крайней мере, если бы ваш код зашел так далеко.

Перед сохранением вашей модели Rails запустит все проверки на ней, включая проверку уникальности кода реферала. Для этого он запустит оператор SQL для проверки базы данных; если вы загляните в файл development.log, вы обязательно увидите этот запрос.

Так что Rails здесь работает правильно.

Если ваши реферальные коды должны быть уникальными, то почему бы и нет? Я думаю, что вы пытаетесь сохранить модели с нулевым или пустым кодом. Если это так, попробуйте добавить :allow_nil => true или :allow_blank => true в хеш: uniqueness.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...