Деконструирование Rails.joins &.where методы

У меня две модели - Banner и BannerType.

Их схема выглядит так:

Баннер

# Table name: banners
#
#  id             :integer          not null, primary key
#  name           :string(255)
#  image          :string(255)
#  created_at     :datetime         not null
#  updated_at     :datetime         not null
#  url            :string(255)
#  banner_type_id :integer

BannerType

# Table name: banner_types
#
#  id         :integer          not null, primary key
#  name       :string(255)
#  created_at :datetime         not null
#  updated_at :datetime         not null

Banner belongs_to :banner_type и BannerType has_many :banners

У меня есть две записи в BannerType:

BannerType.all
  BannerType Load (0.3ms)  SELECT "banner_types".* FROM "banner_types" 
 => [#<BannerType id: 1, name: "Featured", created_at: "2012-12-17 04:35:24", updated_at: "2012-12-17 04:35:24">, #<BannerType id: 2, name: "Side", created_at: "2012-12-17 04:35:40", updated_at: "2012-12-17 04:35:40">] 

Если я хочу сделать запрос, чтобы найти все баннеры типа Featured, я могу сделать что-то вроде этого:

Banner.joins(:banner_type).where("banner_types.name = ?", 'Featured')

Я знаю, что я мог бы также запросить banner_type_id => 1, но это связано с этим конкретным вопросом.

Если мы сломаем этот оператор, есть несколько вещей, которые меня немного сбивают с толку.

  • Banner.join(:banner_type) - сгенерирует NoMethodError: undefined method 'join' for #<Class:0x007fb6882909f0> Почему нет метода Rails, называемого join, если это имя метода SQL?
  • Почему я делаю Banner.joins(:banner_type), т.е. единственное число banner_type, когда имя таблицы banner_types. Я не присоединяюсь к таблицам Banner и BannerType (которые обозначают Rails как множественное число). Если я попробую Banner.joins(:banner_types), это ошибка, которую я получаю:

    Banner.joins(:banner_types) ActiveRecord::ConfigurationError: Association named 'banner_types' was not found; perhaps you misspelled it?

  • Почему для предложения where требуется banner_types, а не banner_type (т.е. множественная версия - то есть имя таблицы, а не символ, используемый в методе joins?). Кажется, что это будет больше интуитивно понятным, если вы используете имена таблиц в обоих местах или используете имена символов в обоих местах. Если, по крайней мере, для целей последовательности.

  • Почему я не могу выполнять динамический поиск через ассоциации - то есть было бы хорошо, если бы я мог сделать Banner.find_by_banner_type_name("Featured")?

Хотелось бы услышать ваши мысли.

Спасибо.

Ответ 1

# 1

Banner.join(: banner_type) - генерирует NoMethodError: undefined метод "join" для # Почему нет метода Rails, называемого join, когда это имя метода SQL?

Яснее, когда вы читаете как простой английский, чтобы сказать, что "Баннер присоединяется к типу баннера" и "Баннер присоединиться к типу баннера". Я не уверен, что есть больше причин, чем это.


# 2

Почему я делаю Banner.joins(: banner_type), то есть особый параметр banner_type, когда имя таблицы - banner_types. Я не присоединяюсь к таблицам Banner и BannerType (которые обозначают Rails как множественное число). Если я попробую Banner.joins(: banner_types), это ошибка, которую я получаю:

Banner.joins(: banner_types) ActiveRecord:: ConfigurationError: Ассоциация с именем "banner_types" не найдена; возможно, вы его опечалили?

В .joins(:banner_type), :banner_type - это отношение, к которому вы присоединяетесь, а не таблица. У вас

has_one :banner_type

так что Rails объединяется. Это как раз то, как Rails работает, когда вы передаете символ в .joins, и поэтому ошибка относится к ассоциации при передаче символа, не соответствующего какой-либо существующей ассоциации для вашей модели.

Вот почему вы можете сделать JOIN несколько уровней с использованием символов для вложенных ассоциаций, как описано в Rails Guide

Category.joins(:posts => [{:comments => :guest}, :tags])

Также описывается в Rails Guide, вы также можете передать Strings на .joins.

Client.joins('LEFT OUTER JOIN addresses ON addresses.client_id = clients.id')

Примечание в этом последнем примере, когда String передается в joins, используется имя таблицы addresses, а не имя ассоциации; это помогает ответить # 3.


# 3

Почему для предложения where нужны banner_types, а не banner_type (т.е. плюрализуемая версия - то есть имя таблицы, а не символ, используемый в методе соединений)? Кажется, было бы более интуитивно, если бы вы использовали имена таблиц в обоих местах или используйте имена символов в обоих местах. Если, по крайней мере, для целей последовательности.

После немного интерполяции строк строки, переданные методу where (похожие на joins), более или менее передаются непосредственно в конечный SQL-запрос (с помощью ARel будет немного манипулировать), name - неоднозначный столбец (обе таблицы banners и banner_types имеют столбец name), поэтому, ссылаясь на таблицу, требуется полный путь [TABLE NAME].[COLUMN NAME]. Если, например, у вас был столбец color в banner_types (который также не существовал в banners), нет необходимости использовать его как "banner_types.color = ?" в вашем методе where; "color = ?" будет работать нормально.

Примечание. Как и в # 2, вы можете передавать символы в метод where для таблиц JOIN 'd.

Banner.joins(:banner_type).where(banner_type: [name: 'Featured'])

# 4

Почему я не могу выполнять динамическое обнаружение через ассоциации - то есть было бы хорошо, если бы я мог сделать Banner.find_by_banner_type_name ( "Featured" )?

Вы не можете так найти, потому что он не поддерживается в AREL, это так просто (IMO, имя метода, как find_by_banner_type_name, довольно запутывающее).