Ruby on Rails:: включает полиморфную ассоциацию с подмоделями - PullRequest
10 голосов
/ 06 марта 2010

При работе с полиморфной ассоциацией возможно ли запускать подмодели, которые присутствуют только в некоторых типах?

Пример:

class Container
  belongs_to :contents, :polymorphic => true
end
class Food
  has_one :container
  belongs_to :expiration
end
class Things
  has_one :container
end

В представлении я хочу сделать что-то вроде:

<% c = Containers.all %>
<% if c.class == Food %>
  <%= food.expiration %>
<% end %>

Поэтому я бы хотел загрузить истечения при загрузке c, потому что я знаю, что мне понадобится каждый из них. Есть ли способ сделать это? Просто определение регулярного: include вызывает ошибки, потому что не все вложенные типы имеют срок действия субмодели.

1 Ответ

24 голосов
/ 06 марта 2010

Отредактированный ответ

Недавно я обнаружил, что Rails поддерживает активную загрузку полиморфных ассоциаций при фильтрации по столбцу полиморфного типа. Так что нет необходимости объявлять поддельные ассоциации.

class Container
  belongs_to :content, :polymorphic => true
end

Теперь запрос Container по 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)

Старый ответ

Это хак, поскольку нет прямого способа включить полиморфные объекты в один запрос.

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

Теперь запросите Container по 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)

Это приводит к двум вызовам SQL в базу данных (фактически это 4 вызова, поскольку rails выполняет один SQL для каждого :include)

Нет никакого способа сделать это в одном SQL, так как вам нужен другой набор столбцов для разных типов контента.

Предупреждение: Не следует использовать фиктивные ассоциации в классе Content, поскольку это приведет к неожиданным результатам.

Например: допустим, что первый объект в таблице contents содержит пищу.

Content.first.food # will work
Content.first.thing

Второй звонок не будет работать. Это может дать вам объект Thing с тем же идентификатором, что и у объекта Food, на который указывает Content.

...