178

¿Existe alguna manera de validar que un registro real sea único y no solo una columna? Por ejemplo, un modelo de la amistad/tabla no debe ser capaz de tener múltiples registros idénticos como:Validar la unicidad de varias columnas

user_id: 10 | friend_id: 20 
user_id: 10 | friend_id: 20 
+7

perdóneme si estoy siendo muy tupido, pero ¿cómo podría ayudar eso en esta situación? – re5et

+2

intente utilizar "validates_uniqueness_of" en su modelo. si esto no funciona intente crear un índice en el que pueda crear una migración de feilds que incluya una instrucción como add_index: table, [: column_a,: column_b],: unique => true) –

+1

@HarryJoy, preguntó ' ¿Hay un camino de rieles? Y lo ofreces sin rieles, pero estándar. 'La forma Active Record afirma que la inteligencia pertenece a sus modelos, no a la base de datos. – Green

Respuesta

306

Puede alcance una llamada validates_uniqueness_of de la siguiente manera.

validates_uniqueness_of :user_id, :scope => :friend_id 
+78

Solo quería agregar que puede pasar múltiples parámetros de alcance en el caso necesita validar la singularidad en más de 2 campos. Es decir. : scope => [: friend_id,: group_id] –

+25

Es extraño que no puedas decir 'validates_uniqueness_of [: user_id,: friend_id]'. Tal vez esto necesita ser parchado? – Alexey

+11

Alexey, validates_uniqueness_of [: user_id,: friend_id] simplemente hará la validación de cada uno de los campos enumerados, y está documentado y el comportamiento esperado –

32

Es probable que necesite limitaciones reales en la base de datos, porque valida sufre de condiciones de carrera.

validates_uniqueness_of :user_id, :scope => :friend_id 

Cuando usted persiste una instancia de usuario, Rails validar el modelo mediante la ejecución de una consulta SELECT para ver si los registros de usuario que ya existen con el user_id proporcionado. Suponiendo que el registro demuestre ser válido, Rails ejecutará la instrucción INSERT para persistir en el usuario. Esto funciona muy bien si está ejecutando una sola instancia de un único servidor web de procesos/hilos.

En caso de que dos procesos/subprocesos intenten crear un usuario con el mismo ID de usuario al mismo tiempo, puede darse la siguiente situación. Race condition with validates

Con índices únicos en el db en su lugar, la situación anterior se desarrollará de la siguiente manera. Unique indexes on db

respuesta tomado de esta entrada del blog - http://robots.thoughtbot.com/the-perils-of-uniqueness-validations

+3

¿Y cómo conseguimos las restricciones del lado de la base de datos (de restricción única entre columnas múltiples) con el sistema de migración de Rails? –

+0

http://stackoverflow.com/questions/4123610/how-to-implement-a-unique-index-on-two-columns-in-rails –

+0

@LeeHanKyeol para hacer eso, puede hacer algo como def change add_index : users,: email, unique: true end end –

113

Puede utilizar validates para validar uniqueness en una columna:

validates :user_id, uniqueness: {scope: :friend_id} 

La sintaxis para la validación en varias columnas es similar, pero se debe proporcione una matriz de campos en su lugar:

validates :attr, uniqueness: {scope: [:attr1, ... , :attrn]} 

Sin embargo,, los enfoques de validación que se muestran arriba tienen una condición de carrera y no pueden garantizar la coherencia. Considere el ejemplo siguiente: registros de la tabla

  1. base de datos se supone que son únicas por n campos;

  2. múltiples (dos o más ) solicitudes simultáneas, manejadas por procesos separados cada uno (servidores de aplicaciones, servidores de trabajo de fondo o lo que sea que esté utilizando), base de datos de acceso a insertar el mismo registro en la tabla;

  3. cada proceso en paralelo valida si hay un registro con los mismos campos n;

  4. la validación de cada solicitud se pasa satisfactoriamente, y cada proceso crea un registro en la tabla con los mismos datos.

Para evitar este tipo de comportamiento, se debe agregar una restricción única db mesa.Se puede establecer con add_index ayudante para uno (o varios) campo (s) ejecutando el siguiente migración:

class AddUniqueConstraints < ActiveRecord::Migration 
    def change 
    add_index :table_name, [:field1, ... , :fieldn], unique: true 
    end 
end 

Advertencia: incluso después de que haya configurado una restricción única, dos o más solicitudes simultáneas se trate de escribir los mismos datos a DB, pero en lugar de crear registros duplicados, esto elevará una excepción ActiveRecord::RecordNotUnique, que debe manejar por separado:

begin 
# writing to database 
rescue ActiveRecord::RecordNotUnique => e 
# handling the case when record already exists 
end 
+1

¡Gracias, señor! – nuc

0

DB restricción:

add_index :friendships, [:user_id, :friend_id], unique: true