Как перехватить исключение ActiveRecord :: RecordNotFound во время сохранения()?

#ruby-on-rails #ruby #activerecord

#ruby-on-rails #ruby #activerecord

Вопрос:

У меня в базе данных есть таблица Users со столбцом Email. Я также создал УНИКАЛЬНЫЙ индекс в столбце Email, чтобы запретить двум пользователям регистрировать один и тот же адрес электронной почты (примечание: пожалуйста, не предлагайте мне использовать validates_uniqueness_of , поскольку это то, чего я пытаюсь избежать).

Когда я запускаю свой тест RSpec, чтобы убедиться, что дублирующаяся запись не может быть вставлена, я вижу следующую ошибку:

 Failures:
  1) User should not allow duplicate email addresses
     Failure/Error: user2.save.should_not be_true
     ActiveRecord::RecordNotUnique:
       SQLite3::ConstraintException: column email is not unique: INSERT INTO "users" ("email", ... ) VALUES ( ... )
     # ./spec/models/user_spec.rb:26
  

Это хорошо, потому что это означает, что мой УНИКАЛЬНЫЙ индекс действительно работает. Вопрос в том, как я могу обработать это исключение? Я хотел бы иметь возможность перехватывать его, а затем добавлять разумное сообщение в коллекцию ошибок модели.

Я безуспешно пытался использовать rescue_from в контроллере следующим образом:

 rescue_from 'ActiveRecord::RecordNotUnique' do |ex|
    raise 'Email must be unique'
end
  

Документы Rails API, похоже, не предлагают, как переопределить метод save (), чтобы добавить блок begin / rescue, поэтому мой вопрос заключается в следующем: Как я могу обработать исключение ActiveRecord :: RecordNotUnique, которое генерируется во время сохранения (), затем пометить модель как недопустимую и добавить разумное сообщение об ошибке в коллекцию ошибок модели?

Ответ №1:

 class User
...
def save
 super
 rescue 'ActiveRecord::RecordNotUnique' 
   logger.error($!.to_s) # or something like that.
 end
end
  

Вы можете перегрузить любое действие в своих моделях и просто вызвать super для выполнения унаследованного определения метода

Rails API, вероятно, не упоминает об этом, потому что это особенность Ruby, а не только Rails.

Комментарии:

1. Когда я пытаюсь использовать rescue_from в модели, я получаю сообщение об ошибке: неопределенный метод ‘resuce_from’… Мы используем rails 3.0.7, поэтому оно должно быть доступно. Похоже, это то, что доступно только в ApplicationController, а не в ActiveRecord.

2. извините, я немного поленился с копированием и вставкой, это было бы обычным делом rescue я обновлю свой ответ

3. Спасибо. Я придумал нечто подобное, основываясь на вашем предложении: def save begin super #invoke суперкласс save() спасает ActiveRecord::RecordNotUnique #задача: улучшить сообщение об ошибке? ошибки.добавить:email, ‘Этот адрес электронной почты уже используется’ false end завершение

Ответ №2:

У меня была похожая проблема. У меня есть таблица с индексом, использующим несколько полей, по которым отсортирована таблица

в db / migrate

 class CreateDids < ActiveRecord::Migration
  def change
    create_table :dids do |t|
      t.string :lada, null: false, limit: 3
      t.string :pre_did, null: false, limit: 4
      t.string :did, null: false, limit: 7
      t.boolean :uso_interno_ns, default: false, null: false

      t.timestamps
      t.integer :lock_version, null: false, default: 0
      t.index [:lada, :pre_did, :did], unique: true
    end
  end
end
  

Теперь, чтобы проверить уникальное сочетание полей в моделях / did.rb, я написал:

   validates :lada, presence: true, length: { within: 1..3 }, numericality: { only_integer: true}
  validates :pre_did, presence: true, length: { within: 1..4 }, numericality: { only_integer: true}
  validates :did, presence: true, length: { within: 4..7 }, numericality: { only_integer: true}
  validate do
    errors.add :base,I18n.t('dids.numero_menor_10') unless 10 == ( self.lada   self.pre_did   self.did ).size if self.lada and self.pre_did and self.did
  end
  

Но он не проверял дублированный набор полей (lada pre_did did), поэтому в models/did.rb также написал:

 def save
  begin
    super
  rescue ActiveRecord::RecordNotUnique => e
    errors.add(:base,I18n.t('dids.telefono_duplicado'))
    false
  end
end

def update( x )
  begin
    super x
  rescue ActiveRecord::RecordNotUnique => e
    errors.add(:base,I18n.t('dids.telefono_duplicado'))
    false
  end
end
  

Теперь в моем случае, если я не возвращаю false после восстановления, это не сработает.