Ruby on Rails: incluye una asociación polimórfica con submodelos
ruby-on-rails include (1)
Cuando se trabaja con una asociación polimórfica, ¿es posible ejecutar una inclusión en submodelos que solo están presentes en algunos tipos?
Ejemplo:
class Container
belongs_to :contents, :polymorphic => true
end
class Food
has_one :container
belongs_to :expiration
end
class Things
has_one :container
end
En la vista, voy a querer hacer algo como:
<% c = Containers.all %>
<% if c.class == Food %>
<%= food.expiration %>
<% end %>
Por lo tanto, me gustaría cargar las expiraciones cuando cargue c, porque sé que necesitaré cada una de ellas. ¿Hay alguna manera de hacerlo? Simplemente definir un regular: include me da errores porque no todos los tipos incluidos tienen una caducidad de submodelo.
Respuesta editada
Recientemente descubrí que Rails admite una carga ansiosa de asociaciones polimórficas cuando filtra por la columna de tipo polimórfico. Entonces no hay necesidad de declarar asociaciones falsas.
class Container
belongs_to :content, :polymorphic => true
end
Ahora consulta el Container
por container_type
.
containers_with_food = Container.find_all_by_content_type("Food",
:include => :content)
containers_with_thing = Container.find_all_by_content_type("Thing",
:include => :content)
Vieja respuesta
Esto es un truco ya que no hay una forma directa de incluir los objetos polimórficos en una consulta.
class Container
belongs_to :contents, :polymorphic => true
# add dummy associations for all the contents.
# this association should not be used directly
belongs_to :food
belongs_to :thing
end
Ahora consulta el Container
por container_type
.
containers_with_food = Container.find_all_by_content_type("Food",
:include => :food)
containers_with_thing = Container.find_all_by_content_type("Thing",
:include => :thing)
Eso resulta en dos llamadas SQL a la base de datos (en realidad son 4 llamadas ya que rails ejecuta un SQL por cada :include
)
No hay forma de hacer esto en un SQL ya que necesita diferentes conjuntos de columnas para diferentes tipos de contenido.
Advertencia: las asociaciones ficticias en la clase de Content
no se deben usar directamente, ya que generarán resultados inesperados.
Por ejemplo: digamos que el primer objeto en la tabla de contents
contiene comida.
Content.first.food # will work
Content.first.thing
La segunda llamada no funcionará. Podría darle un objeto Thing
con la misma identificación que el objeto Food
señalado por el Content
.