2010-12-09 10 views
7

Tengo un índice único en múltiples campos en mi base de datos. Por lo tanto, si intentas guardar en un registro duplicado, activa ActiveRecord :: StatementInvalid y muestra el error mysql. ¿Hay alguna manera de manejar esto dentro de los rieles creando la restricción única o haciendo que devuelva un mensaje de error relevante cuando esto sucede?¿Cómo puedo hacer que Ruby on Rails maneje errores de registro duplicados de MySQL?

heres la traza:

ActiveRecord::StatementInvalid: Mysql::Error: Duplicate entry '2010-12-09-2-0-1-1' for key 2: INSERT INTO `entries` (`rejected_at`, `created_at`, `comments`, `overtime`, `submitted_at`, `updated_at`, `time`, `approved`, `day`, `user_id`, `approved_at`, `job_id`, `submitted`, `rejected`) VALUES(NULL, '2010-12-09 21:50:46', NULL, 0, NULL, '2010-12-09 21:50:46', 2.0, NULL, '2010-12-09', 1, NULL, 1, NULL, NULL) 
from /home/cmatthews/src/cannon/vendor/rails/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:219:in `log' 
from /home/cmatthews/src/cannon/vendor/rails/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb:319:in `execute' 
from /home/cmatthews/src/cannon/vendor/rails/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:259:in `insert_sql' 
from /home/cmatthews/src/cannon/vendor/rails/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb:329:in `insert_sql' 
from /home/cmatthews/src/cannon/vendor/rails/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:44:in `insert_without_query_dirty' 
from /home/cmatthews/src/cannon/vendor/rails/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb:18:in `insert' 
from /home/cmatthews/src/cannon/vendor/rails/activerecord/lib/active_record/base.rb:2901:in `create_without_timestamps' 
from /home/cmatthews/src/cannon/vendor/rails/activerecord/lib/active_record/timestamp.rb:53:in `create_without_callbacks' 
from /home/cmatthews/src/cannon/vendor/rails/activerecord/lib/active_record/callbacks.rb:266:in `create' 
from /home/cmatthews/src/cannon/vendor/rails/activerecord/lib/active_record/base.rb:2867:in `create_or_update_without_callbacks' 
from /home/cmatthews/src/cannon/vendor/rails/activerecord/lib/active_record/callbacks.rb:250:in `create_or_update' 
from /home/cmatthews/src/cannon/vendor/rails/activerecord/lib/active_record/base.rb:2538:in `save_without_validation' 
from /home/cmatthews/src/cannon/vendor/rails/activerecord/lib/active_record/validations.rb:1078:in `save_without_dirty' 
from /home/cmatthews/src/cannon/vendor/rails/activerecord/lib/active_record/dirty.rb:79:in `save_without_transactions' 
from /home/cmatthews/src/cannon/vendor/rails/activerecord/lib/active_record/transactions.rb:229:in `send' 
from /home/cmatthews/src/cannon/vendor/rails/activerecord/lib/active_record/transactions.rb:229:in `with_transaction_returning_status' 
from /home/cmatthews/src/cannon/vendor/rails/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:136:in `transaction' 
from /home/cmatthews/src/cannon/vendor/rails/activerecord/lib/active_record/transactions.rb:182:in `transaction' 
from /home/cmatthews/src/cannon/vendor/rails/activerecord/lib/active_record/transactions.rb:228:in `with_transaction_returning_status' 
from /home/cmatthews/src/cannon/vendor/rails/activerecord/lib/active_record/transactions.rb:196:in `save' 
from /home/cmatthews/src/cannon/vendor/rails/activerecord/lib/active_record/transactions.rb:208:in `rollback_active_record_state!' 
from /home/cmatthews/src/cannon/vendor/rails/activerecord/lib/active_record/transactions.rb:196:in `save' 

Respuesta

4

Puede agregar una restricción de exclusividad a su modelo para que el registro regrese con errores y no sea válido.

Por ejemplo (carriles 2):

class User < ActiveRecord::Base 
    validates_uniqueness_of :email, :scope => [:name, :age] 
end 

o (carriles 3)

class User < ActiveRecord::Base 
    validates :email, :uniqueness => {:scope => [:name, :age]} 
end 

Esto debe resultar en lo siguiente:

user1 = User.create :email => "[email protected]", :name => "one", :age => 20 
user2 = User.create :email => "[email protected]", :name => "one", :age => 20 
user2.valid? # false 
+0

, sé que puedo hacer eso para un campo, pero ¿puedo hacerlo para varios, como puedo tener que verificar para ver si tienen el mismo: correo electrónico, nombre, color favorito y trabajo. Puede haber 5 personas llamadas John, pero solo una puede tener [email protected], naranja, y ser un programador. ¿Tiene sentido? – loosecannon

+1

Oye, actualicé la publicación con la respuesta para múltiples atributos. –

+6

Si bien eso cubre la mayoría de los casos de uso, siempre ha existido una condición de carrera en validates_uniqueness_of - no detiene por completo las entradas duplicadas debido a la forma en que funciona. –

1

¿Puede cambiar la consulta a

INSERT IGNORE INTO `entries` ... 

Alternativamente, usted podría considerar INSERT... ON DUPLICATE KEY UPDATE....

La documentación INSERT es here.

+0

Creo que de alguna manera se resolvería el problema, pero la consulta SQL se genera automáticamente. Podría modificarlo, excepto que quiero que genere un error, solo uno que pueda detectar y tener un mensaje, esto solo hace que el servidor diga 500 errores internos del servidor. – loosecannon

11
begin 
    Event.create!(:name => 'Ironman Lanzarote 2011') 
rescue ActiveRecord::RecordNotUnique => e 
    # handle duplicate entry 
end 

Esto funciona para yo bajo Rails 3.

+1

Esto viola el principio de menos asombro y no sigue las convenciones en Rails. – maletor

+1

Cuando utiliza una configuración maestro/esclavo, se encontrará con problemas de deriva (y las condiciones de carrera resultantes), que pueden resolverse fácilmente utilizando este enfoque, en lugar de utilizar la validación exclusiva de los rieles, que podría verificarlo en el esclavo. – scones

+0

Esto está bien, pero pondría este comportamiento en un método modelo con un nombre descriptivo. – Papipo

Cuestiones relacionadas