I've got a Users table with an Email column in my database. I've also created a UNIQUE index on the Email column to prevent two users from registering the same email address (note: please don't suggest that I use validates_uniqueness_of
since this is what I'm trying to avoid).
When I run run my RSpec test to make sure that a duplicate record cannot be inserted, I see the following error:
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
This is good because it means that my UNIQUE index is indeed working. The question is, how can I handle this exception? I'd like to be able to catch it, then add开发者_C百科 a sensible message to the model's errors collection.
I've tried - unsuccessfully - using rescue_from in the controller as follows:
rescue_from 'ActiveRecord::RecordNotUnique' do |ex|
raise 'Email must be unique'
end
The Rails API docs don't appear to suggest how to override the save() method in order to add a begin/rescue block, so my question is this: How can I handle the ActiveRecord::RecordNotUnique exception that is being thrown during save() then mark the model as invalid and add a sensible error message to the model's errors collection?
class User
...
def save
super
rescue 'ActiveRecord::RecordNotUnique'
logger.error($!.to_s) # or something like that.
end
end
You can overload any action in your models and just call super
to execute the inherited method definition
The Rails API prolly doesn't mention it because its an feature of Ruby, not just Rails.
I had a similar problem. I have a table with an index using several fields up by wich the table is sorted
at 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
Now, to validate a unique mix of fields in models/did.rb I wrote:
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
But, it did not validate for duplicated mix of fields (lada+pre_did+did), so in models/did.rb also wrote:
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
Now in my case, if I do not return false after rescue, this does not work.
精彩评论