ruby-on-rails - many - rails model references
HABTM-restricción de singularidad (4)
Tengo dos modelos con una relación HABTM: usuario y rol.
- usuario - has_and_belongs_to_many: roles
- rol - corresponde_a: usuario
Quiero agregar una restricción de unicidad en la unión (tabla users_roles) que dice que user_id y role_id deben ser únicos. En Rieles, se vería como:
validates_uniqueness_of :user, :scope => [:role]
Por supuesto, en Rails, generalmente no tenemos un modelo para representar la relación de unión en una asociación HABTM.
Entonces mi pregunta es ¿dónde está el mejor lugar para agregar la restricción?
Creo que usar: uniq => true aseguraría que no obtendrás objetos duplicados. Pero, si desea verificar si existe un duplicado antes de escribir un segundo en su db, probablemente usaría find_or_create_by_name_and_description (...).
(Por supuesto, el nombre y la descripción son sus valores de columna)
En Rails 5 querrás usar distinct
lugar de uniq
También, intente esto para asegurar la singularidad
has_and_belongs_to_many :foos, -> { distinct } do
def << (value)
super value rescue ActiveRecord::RecordNotUnique
end
end
Puedes agregar singularidad para unirte a la tabla
add_index :users_roles, [ :user_id, :role_id ], :unique => true, :name => ''by_user_and_role''
Tu base de datos generará una excepción, que debes manejar.
No conozco ninguna validación de rieles lista para usar para este caso, pero puede agregar su propia validación de esta manera:
class User < ActiveRecord::Base
has_and_belongs_to_many :roles, :before_add => :validates_role
Simplemente soltaría silenciosamente la llamada a la base de datos y reportaría el éxito.
def validates_role(role)
raise ActiveRecord::Rollback if self.roles.include? role
end
ActiveRecord :: Rollback se captura internamente pero no se vuelve a subir.
Editar
No use la parte donde estoy agregando validación personalizada . Funciona un poco pero hay mejores alternativas.
Use la opción :uniq
en asociación como @Spyros sugirió en otra respuesta:
class Parts < ActiveRecord::Base
has_and_belongs_to_many :assemblies, :uniq => true, :read_only => true
end
(este fragmento de código es de Rails Guides v.3). Lea en Rails Guides v 3.2.13 busque 4.4.2.19: uniq
Rails Guide v.4 específicamente advierte contra el uso de include?
para comprobar la singularidad debido a las posibles condiciones de la raza.
La parte sobre la adición de un índice para unirse a la tabla permanece.
yo prefiero
class User < ActiveRecord::Base
has_and_belongs_to_many :roles, -> { uniq }
end
otras opciones de referencia here