解构Rails .joins和.where方法

mar*_*ion 12 ruby-on-rails arel ruby-on-rails-3

我有两个型号 - BannerBannerType.

他们的架构如下所示:

旗帜

# 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
Run Code Online (Sandbox Code Playgroud)

BannerType

# Table name: banner_types
#
#  id         :integer          not null, primary key
#  name       :string(255)
#  created_at :datetime         not null
#  updated_at :datetime         not null
Run Code Online (Sandbox Code Playgroud)

Banner belongs_to :banner_typeBannerType 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">] 
Run Code Online (Sandbox Code Playgroud)

如果我想查询以查找所有类型的横幅,Featured我可以执行以下操作:

Banner.joins(:banner_type).where("banner_types.name = ?", 'Featured')
Run Code Online (Sandbox Code Playgroud)

我知道我也可以查询,banner_type_id => 1但这与这个特定问题密切相关.

如果我们打破这种说法,有一些事情对我来说有点混乱.

  1. Banner.join(:banner_type)- 会生成NoMethodError: undefined method 'join' for #<Class:0x007fb6882909f0>为什么join在SQL方法名称时没有调用Rails方法?
  2. 为什么我这样做,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?

  3. 为什么where子句需要banner_types而不是banner_type(即复数版本 - 即表名而不是joins方法中使用的符号?如果你在两个地方都使用表名或者在两个地方都使用符号名,那么它会更直观至少,为了一致性目的.

  4. 为什么我不能通过关联进行动态发现 - 也就是说,如果我能这样做会很好Banner.find_by_banner_type_name("Featured")吗?

很想听听你的想法.

谢谢.

dee*_*our 9

#1

Banner.join(:banner_type) - 将生成NoMethodError:未定义的方法'join'为#为什么没有Rails方法称为连接,这是SQL方法名称?

当以简单的英语阅读"Banner加入横幅类型"与"横幅加入横幅类型"时,它更清晰.我不确定是否有更多理由.


#2

为什么我做Banner.joins(:banner_type),即表名为banner_types时的单数banner_type.我没有加入Banner和BannerType表(Rails约定表示为复数).如果我尝试Banner.joins(:banner_types),这是我得到的错误:

Banner.joins(:banner_types)ActiveRecord :: ConfigurationError:找不到名为'banner_types'的关联; 也许你拼错了吗?

.joins(:banner_type),:banner_type你正在加入的关系,而不是表格.你有

has_one :banner_type
Run Code Online (Sandbox Code Playgroud)

这就是Rails加入的内容.这就是当您将符号传递给Rails时Rails的工作原理,这就是当您传递符号与.joins模型的任何现有关联不匹配时错误引用关联的原因.

这也是为什么你能够JOIN使用嵌套关联的符号深入多层次的原因,如Rails指南中所述

Category.joins(:posts => [{:comments => :guest}, :tags])
Run Code Online (Sandbox Code Playgroud)

也可以在Rails指南中进行描述,也可以将字符串传递给.joins它.

Client.joins('LEFT OUTER JOIN addresses ON addresses.client_id = clients.id')
Run Code Online (Sandbox Code Playgroud)

请注意,在最后一个示例中,当传递String时joins,使用表名 addresses,而不是关联名; 这有助于回答#3.


#3

为什么where子句需要banner_types而不是banner_type(即复数版本 - 即表名而不是连接方法中使用的符号?如果你在两个地方使用表名或使用符号,它会更直观两个地方的名称.至少,为了一致性目的.

经过一些字符串插值后,传递给where方法的字符串(类似于joins)或多或少地直接传递到最终的SQL查询中(ARel会在此过程中进行一些操作).name是一个模糊的列(你bannersbanner_types表都有一name列),所以需要通过它的完整路径[TABLE NAME].[COLUMN NAME]来引用表.例如,如果您有一些colorbanner_types (也不存在banners),则不需要像"banner_types.color = ?"在您的where方法中那样使用它; "color = ?"会工作得很好.

注意,就像在#2中一样,您可以将符号传递whereJOIN'd表的方法.

Banner.joins(:banner_type).where(banner_type: [name: 'Featured'])
Run Code Online (Sandbox Code Playgroud)

#4

为什么我不能通过关联进行动态查找 - 即如果我能做Banner.find_by_banner_type_name("精选")会很好吗?

你不能做那样的发现,因为它在ARel中不受支持,它就是那么简单(IMO,一个方法名称,比如find_by_banner_type_name令人困惑).