Validar la singularidad de múltiples columnas
-
28-10-2019 - |
Pregunta
¿Existe una forma de rieles de validar que un registro real es único y no solo una columna? Por ejemplo, un modelo / tabla de amistad no debería poder tener múltiples registros idénticos como:
user_id: 10 | friend_id: 20
user_id: 10 | friend_id: 20
Solución
Puedes alcanzar un validates_uniqueness_of
Llame de la siguiente manera.
validates_uniqueness_of :user_id, :scope => :friend_id
Otros consejos
Puedes usar validates
validar uniqueness
En una columna:
validates :user_id, uniqueness: {scope: :friend_id}
La sintaxis para la validación en varias columnas es similar, pero debe proporcionar 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 consistencia. Considere el siguiente ejemplo:
Se supone que los registros de la tabla de bases de datos son únicos por norte campos;
múltiple (dos o más) solicitudes concurrentes, manejadas por procesos separados cada uno (servidores de aplicaciones, servidores de trabajadores de fondo o lo que sea que esté utilizando), acceder a la base de datos para insertar el mismo registro en la tabla;
Cada proceso en paralelo valida si hay un registro con el mismo norte campos;
La validación para cada solicitud se pasa correctamente, y cada proceso crea un registro en la tabla con los mismos datos.
Para evitar este tipo de comportamiento, uno debe agregar un restricción única a la mesa DB. Puedes configurarlo con add_index
Ayudante para un campo (o múltiple) campo ejecutando la siguiente migración:
class AddUniqueConstraints < ActiveRecord::Migration
def change
add_index :table_name, [:field1, ... , :fieldn], unique: true
end
end
Advertencia : Incluso después de establecer una restricción única, dos o más solicitudes concurrentes intentarán escribir los mismos datos en DB, pero en lugar de crear registros duplicados, esto elevará un ActiveRecord::RecordNotUnique
excepción, que debe manejar por separado:
begin
# writing to database
rescue ActiveRecord::RecordNotUnique => e
# handling the case when record already exists
end
Esto se puede hacer con una restricción de base de datos en las dos columnas:
add_index :friendships, [:user_id, :friend_id], unique: true
Puede usar un validador de Rails, pero en general recomiendo usar una restricción de base de datos.
Más lectura: https://robots.thoughtbot.com/validation-database-constraint-or-both