16

Tengo dos modelos con una relación HABTM: usuario y rol.HABTM - restricción de exclusividad

  • usuario - has_and_belongs_to_many: papeles
  • papel - belongs_to: usuario

Quiero añadir una restricción de unicidad en la (tabla) users_roles unirse a que dice que el user_id y ROLE_ID deben ser únicos. En los carriles, se vería así:

validates_uniqueness_of :user, :scope => [:role] 

Por supuesto, en los carriles, que normalmente no tenemos un modelo para representar la relación unirse en una asociación HABTM.

Entonces mi pregunta es ¿dónde está el mejor lugar para agregar la restricción?

Respuesta

36

Usted puede agregar singularidad a unirse a la mesa

add_index :users_roles, [ :user_id, :role_id ], :unique => true, :name => 'by_user_and_role' 

ver In a join table, what's the best workaround for Rails' absence of a composite key?

Su base de datos lanzará una excepción a continuación, que usted tiene manejar.
no sé ninguna lista para utilizar la validación de los carriles para este caso, pero se puede añadir su validación como esto:

class User < ActiveRecord::Base 
has_and_belongs_to_many :roles, :before_add => :validates_role 

me acaba de caer en silencio la llamada base de datos e informar éxito.

def validates_role(role) 
    raise ActiveRecord::Rollback if self.roles.include? role 
end 

ActiveRecord :: Rollback es capturado internamente pero no resubía.

Editar

No utilice la parte en la que estoy añadiendo una validación personalizada. Funciona un poco, pero hay mejores alternativas.

Uso :uniq opción asociación como @Spyros sugirió en otra respuesta:

class Parts < ActiveRecord::Base 
    has_and_belongs_to_many :assemblies, :uniq => true, :read_only => true 
end 

(fragmento de este código es de Rieles Guías de la versión 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 exclusividad debido a las posibles condiciones de carrera.

Se mantiene la parte sobre cómo agregar un índice para unir la tabla.

+0

¡Gracias por esto! Terminé usando esto para una instancia HABTM de un usuario y grupos (esencialmente el mismo tipo de configuración de roles). – dennismonsewicz

+1

Es un poco desagradable que la deduplicación no se maneje automáticamente para las asociaciones de habtm, a diferencia de ': has_many'. – prusswan

+1

publicado después de la edición. He publicado esta respuesta y se me olvidó. Y luego me encontré con un problema relacionado y recordé que tengo una respuesta, la gente mantiene la votación ascendente y la revisé y ¡OMG! ¡Qué estaba pensando! Espero que nadie tenga ningún problema porque usó mi respuesta. Me siento lo suficientemente mal sin eso. Bueno, esa es una lección para todos nosotros. Dejas una respuesta descuidada con prisa en Stackoverflow y te olvidas de ella, pero llegará el momento en que saltará y te morderá por el culo. –

5

Creo que el uso de: uniq => true aseguraría que no obtenga objetos duplicados. Pero, si desea verificar si existe un duplicado antes de escribir un segundo para su base de datos, probablemente usaría find_or_create_by_name_and_description (...).

(De nombre del curso y la descripción son sus valores de columna)

5

prefiero Referencia

class User < ActiveRecord::Base 
    has_and_belongs_to_many :roles, -> { uniq } 
end 

otras opciones here

+0

Probándome de esta manera me da el error número de argumentos incorrectos 1 para o – inquisitive

+0

está disponible para raíles 4, no para raíles 3 –

+0

Pero esta validación está en el nivel de modelo solamente. La validación del nivel de base de datos es necesaria para manejar solicitudes concurrentes – Tachyons

4

En los carriles 5 querrá utilizar distinct en lugar de uniq

Además, trate de esto para asegurar la exclusividad

has_and_belongs_to_many :foos, -> { distinct } do 
    def << (value) 
    super value rescue ActiveRecord::RecordNotUnique 
    end 
end 
+0

Funciona, pero no se ve muy bonito, ¿verdad? – Fritzz

+1

+1 para el rescate automático Errores únicos. no es necesario duplicar HABTM para evitar guardar otros atributos – TheRealMrCrowley

Cuestiones relacionadas