Asociación de Rails "has_many" con clave externa NULL
Frecuentes
Visto 3,601 veces
4
Nuestra aplicación Rails 3 tiene modelos Person
y Message
. Los mensajes pueden ser específicos para una Persona (cuando el mensaje person_id
se establece la columna) o pueden ser "globales" (cuando el person_id
columna es NULL).
Nos gustaría tener un sencillo has_many
relación usando el :conditions
opción como tal:
class Person < ActiveRecord::Base
has_many :messages,
:conditions => proc { ['(messages.person_id IS NULL) OR ' +
'(messages.person_id = ?)'], self.id }
# ...
end
Pero parece que el has_many
El método de clase codifica la opción "condiciones" como una cláusula lógica "Y" después de aplicar la restricción de clave externa de igualdad a la identificación del objeto Persona (por ejemplo, "FROM messages WHERE person_id=123 AND (person_id IS NULL OR person_id=123)
"). Parece que no hay forma de permitir que los objetos asociados con claves foráneas nulas pertenezcan a tales asociaciones.
¿Rails 3/ActiveRecord proporciona una forma de hacer esto o debo hackear mis propios métodos de asociación?
2 Respuestas
1
No puede tener una cláusula OR como desea usar condiciones en la asociación ActiveRecord. Puede tener la asociación sin condiciones y luego agregar un método para incluir los mensajes globales que desee. De esa manera, aún podría aprovechar la asociación al crear registros asociados.
# person.rb
has_many :messages
def all_messages
Message.where('messages.person_id=? OR messages.person_id IS NULL', id)
end
Aquí está mi enchufe estándar para el Chillido gem, que es útil para consultas más avanzadas como esta si no desea tener bits de SQL en su código. Echale un vistazo.
Respondido el 07 de Septiembre de 12 a las 13:09
0
Creo que tiene razón, podría renunciar a has_many y crear un alcance con una combinación externa, algo como:
class Person < ActiveRecord::Base
scope :messages lambda {
joins("RIGHT OUTER Join messages on persons.id = messages.person_id")
.where('persons.id = ?',self.id)
}
contestado el 03 de mayo de 12 a las 17:05
No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas ruby-on-rails ruby-on-rails-3 associations or haz tu propia pregunta.
desafortunadamente, esto significa que no puede hacer cosas como: Person.limit(2).includes(:all_messages), ya que all_messages no es una asociación. - joe van dyk
@JoeVanDyk Sí, tienes razón. Sin embargo, hacer include(:all_messages) ejecutaría una consulta SQL adicional para realizar la carga ansiosa. Entonces, más allá de la sintaxis, no hay una diferencia real en términos de rendimiento con respecto a mi sugerencia. - Mago de Ogz
Hacer el include(:all_messages) da como resultado un máximo de una consulta adicional. Si hizo Person.limit(100).includes(:all_messages), esto da como resultado dos consultas. Si usó #all_messages arriba, serían 101 consultas para cargar todo. - joe van dyk