Неправильный sql, сгенерированный ActiveRecord для has_many: через связь с STI - PullRequest
2 голосов
/ 23 марта 2011

Рассмотрим следующие модели:

class First < ActiveRecord::Base
  has_many :tags
  has_many :thirds, :through => :tags
end

class Second < ActiveRecord::Base
end

class Third < Second
  has_many :tags
  has_many :firsts, :through => :tags
end

class Tag < ActiveRecord::Base
  belongs_to :first
  belongs_to :third
end

Другими словами, у нас есть отношение has_many: through 'tag-style', но одна из моделей (третья) является STI, унаследованной от другой (вторая)).

Скажем, я хотел сделать объединение, чтобы увидеть все экземпляры Third для некоторого значения First:

@thirds = Third.joins(:firsts).where("first.id = 2")

Это будет работать как ожидалось;сгенерированный sql (через to_sql):

SELECT `seconds`.* FROM `seconds`
INNER JOIN `tags` ON `seconds`.`id` = `tags`.`third_id`
INNER JOIN `firsts` ON `firsts`.`id` = `tags`.`first_id`
WHERE `seconds`.`type` = 'Third' AND (first.id = 1)

Это не работает в другом направлении:

@firsts = First.joins(:thirds).where("second.id = 2")

Сгенерированный SQL:

SELECT `firsts`.* FROM `firsts` 
INNER JOIN `tags` ON `firsts`.`id` = `tags`.`first_id` 
INNER JOIN `seconds` ON `seconds`.`type` = 'Third'
WHERE (second.id = 2)

Это приводит к дублированию тегов из-за того, что: секунды неправильно соединяются с таблицей тегов, как в первом случае выше (см. Третью строку выражения sql в каждом случае).Все первые теги будут отображаться в итоговой таблице, причем предложение WHERE совершенно неэффективно.

Если нужно указать что-то еще, я не сталкивался с этим.Если кто-нибудь знает, как заставить это работать, пожалуйста, дайте мне знать.В противном случае я предполагаю, что это ошибка в Rails.О, и, пожалуйста, не предлагайте использовать старые методы model.find (), которые должны быть устаревшими.

ОБНОВЛЕНИЕ:

https://rails.lighthouseapp.com/projects/8994-ruby-on-rails/tickets/6608-generated-sql-for-has_many-through-relation-wrong-when-used-with-sti

парню, который подтвердил, что это ошибка, «если вы определите ассоциацию в базовом классе, она будет работать, как ожидалось».Кто-нибудь знает, что это значит / как это сделать?

1 Ответ

1 голос
/ 23 марта 2011

Небольшие изменения в ваших запросах:

@thirds = Third.joins(:firsts).where(:firsts => {:id => 2})
@firsts = First.joins(:thirds).where(:thirds => {:id => 2})

Я думаю, вы должны попытаться добавить к своей модели ИППП кое-что странное:

class Third < Second
  has_many :tags
  has_many :firsts, :through => :tags
  def self.model_name
    name = "seconds"
    name.instance_eval do
      def plural;   pluralize;   end
      def singular; singularize; end
      def i18n_key; singularize; end
      def human(*args); singularize; end
    end
    return name
  end
end

и проверь это отличное чтение http://code.alexreisner.com/articles/single-table-inheritance-in-rails.html

или, как вы можете прочитать по этой ссылке, вы можете использовать этот метод, который выглядит немного чище

def self.inherited(child)
  child.instance_eval do
    def model_name
      Second.model_name
    end
  end
  super
end
...