#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 после восстановления, это не сработает.