【问题标题】:Rails 5.1 where query on has many modelRails 5.1 查询有很多模型
【发布时间】:2018-03-07 22:35:08
【问题描述】:

上下文: 我有两个模型:

gin.rb

class Gin < ApplicationRecord
  has_many :gins_botanical
  has_many :botanicals, through: :gins_botanical

botanical.rb

class Botanical < ApplicationRecord
  has_many :gins_botanical
  has_many :gins, through: :gins_botanical

在查看给定植物药的展示页面时,我想显示一个包含该特定植物药的杜松子酒列表。

我很想使用 where 子句来实现这一点,但我无法让它发挥作用。

PG::UndefinedTable: 错误: 缺少表的 FROM 子句条目 “植物药”第 1 行:选择“杜松子酒”。* 从“杜松子酒”WHERE (botanicals.name LIKE '%an... ^ : SELECT "gins".* FROM "gins" WHERE (botanicals.name LIKE '%angelica root%')

我可以看到"%#{@botanical.name}%" 很好,因为它显示了当前植物药的价值,在这种情况下为angelica root,但显然我没有通过其他也有angelica root 作为植物药的杜松子酒.

botanicals_controller

  def show
    @similargins = Gin.where("botanicals.name LIKE ?", "%#{@botanical.name}%")
  end

botanical/show.html.erb

<% @similargins.each do |gin| %>
  <%= gin.name%>
<% end %>

【问题讨论】:

  • GinBotanical 模型在哪里?为什么你说has_many :gins_botanical而不是has_many :gin_botanicals
  • 您的架构也可能有帮助。
  • @similargins = Gin.includes(:botanicals).references(:botanicals).where("botanicals.name LIKE ?", "%#{@botanical.name}%") 应该可以解决问题
  • 这个问题有点令人困惑,因为您说您想要“包含特定植物的杜松子酒列表”但您使用LIKE。根据您的botanicals 列表,LIKE 可能会返回不包括@botanical 的杜松子酒。而且,从杜松子酒饮用者的角度来看,考虑植物是否因为它们的名称或它们的风味特征而相似是很有趣的。如果是后者,匹配相似的名称可能会返回从风味角度来看根本不相似的植物。
  • 你解决了吗?

标签: ruby-on-rails where-clause


【解决方案1】:

假设您的关联设置正确(这一点也不明显),那么我想您可以执行以下操作:

class Gin < ApplicationRecord 
  has_many :gin_botanicals 
  has_many :botanicals, through: :gin_botanicals

  class << self 

    def with_botanicals(botanicals)
      joins(:bontanicals).where(botanicals: {id: botanicals})
    end

  end

end

我已经说过了,我会再说一遍。有些人会更喜欢scope 而不是类方法。有些人会更喜欢self.with_botanicals 而不是class &lt;&lt; self。可以说,这些都是偏好问题。

然后,假设您有一个 @gin 变量和一个 @botanical 变量。您应该能够执行以下操作:

Gin.with_botanicals(@botanical).where.not(@gin)

这应该为您提供所有具有@botanical 但不是@gin 的杜松子酒。您可以通过以下方式匹配所有 @gin.botanicals

Gin.with_botanicals(@gin.botanicals).where.not(@gin)

如果您想找到与@gin 有共同任何植物成分的所有杜松子酒,那么这是一种稍有不同的鱼锅(涉及使用IN 子句),适用于一个新问题。同样,您可以搜索所有含有植物成分的杜松子酒,名称为 LIKE @gin.bontanicals.pluck(:name),但又是另一类问题。

【讨论】:

  • 模型上的类方法返回ActiveRecord::Relation 对象真的 看起来它可能是scope 而不是(IMO)
  • @MrYoshiji - 是的,确实(在我的回答中承认)。我只是挂断了指南,该指南指出,“使用类方法是接受范围参数的首选方式。”
  • 请问您是哪个指南规定的?官方 Rails 指南?我对此感到惊讶。
  • Yessir,活动记录查询接口的官方 Rails 指南。你可以在section 14.1 - "Passing in arguments"的最后一段找到它。 (顺便说一句,您的个人资料图片是我在最近的一个项目中隐藏的复活节彩蛋的直接灵感。谢谢!)
【解决方案2】:

查看给定植物药的展示页面时,我想显示一个包含该特定植物药的杜松子酒列表。

简单。就这样吧:

@botanical = Botanical.includes(:gins).find(params[:id])
@gins = @botanical.gins

如果您已经有 id,则无需 LIKE 查询。而且由于您已经设置了间接关联,因此您可以使用它来获取杜松子酒。

如果您真正想要的是获得与给定杜松子酒具有共同植物成分的其他杜松子酒,您可以这样做:

class Gin < ApplicationRecord
  has_many :gin_botanicals
  has_many :botanicals, through: :gin_botanicals

  def similiar_gins
    Gin.joins(:botanicals)
     .where(botanicals: { id: self.botanical_ids })
     .where.not(id: self.id)
  end
end

.joins 创建一个左内连接 - 因此连接表中没有匹配的所有行都将被丢弃。

.where(botanicals: { id: self.botanical_ids }) 创建一个WHERE IN 查询,要求连接的记录至少有一个共同的植物。

您还可以使用 GROUP BY 和 HAVING 设置所需的相似度:

class Gin < ApplicationRecord
  has_many :gin_botanicals
  has_many :botanicals, through: :gin_botanicals

  def similiar_gins(common_ingredients: 1)
    Gin.joins(:botanicals)
     .where(botanicals: { id: self.botanical_ids })
     .where.not(id: self.id)
     .group("gins.id")
     .having("COUNT(distinct botanicals.id) >= ?", common_ingredients)
  end
end

给定:

irb(main):039:0> Gin.all.pluck(:id, :name)
   (1.1ms)  SELECT "gins"."id", "gins"."name" FROM "gins"
=> [[1, "Beefeater Gin"], [2, "Bombay Sapphire"], [3, "Mockinghamshire"]]
irb(main):040:0> Botanical.all.pluck(:id, :name)
   (1.1ms)  SELECT "botanicals"."id", "botanicals"."name" FROM "botanicals"
=> [[1, "Almond"], [2, "liquorice"], [3, "Foo"]]
irb(main):041:0> GinBotanical.all.pluck(:gin_id, :botanical_id)
   (0.5ms)  SELECT "gin_botanicals"."gin_id", "gin_botanicals"."botanical_id" FROM "gin_botanicals"
=> [[1, 1], [2, 1], [1, 3], [1, 2], [2, 2]]

含有 2 种常见成分:

irb(main):036:0> Gin.first.similiar_gins(common_ingredients: 2)
  Gin Load (1.2ms)  SELECT  "gins".* FROM "gins" ORDER BY "gins"."id" ASC LIMIT $1  [["LIMIT", 1]]
   (4.0ms)  SELECT "botanicals".id FROM "botanicals" INNER JOIN "gin_botanicals" ON "botanicals"."id" = "gin_botanicals"."botanical_id" WHERE "gin_botanicals"."gin_id" = $1  [["gin_id", 1]]
  Gin Load (4.3ms)  SELECT "gins".* FROM "gins" INNER JOIN "gin_botanicals" ON "gin_botanicals"."gin_id" = "gins"."id" INNER JOIN "botanicals" ON "botanicals"."id" = "gin_botanicals"."botanical_id" WHERE "botanicals"."id" IN (1, 2, 3) AND ("gins"."id" != $1) GROUP BY gins.id HAVING (COUNT(distinct botanicals.id) >= 2)  [["id", 1]]
=> #<ActiveRecord::Relation [#<Gin id: 2, name: "Bombay Sapphire", created_at: "2018-03-07 23:44:43", updated_at: "2018-03-07 23:44:43">]>

但如果我们将其设置为 3,我们会得到一个空集:

irb(main):037:0> Gin.first.similiar_gins(common_ingredients: 3)
  Gin Load (0.7ms)  SELECT  "gins".* FROM "gins" ORDER BY "gins"."id" ASC LIMIT $1  [["LIMIT", 1]]
   (1.8ms)  SELECT "botanicals".id FROM "botanicals" INNER JOIN "gin_botanicals" ON "botanicals"."id" = "gin_botanicals"."botanical_id" WHERE "gin_botanicals"."gin_id" = $1  [["gin_id", 1]]
  Gin Load (5.0ms)  SELECT "gins".* FROM "gins" INNER JOIN "gin_botanicals" ON "gin_botanicals"."gin_id" = "gins"."id" INNER JOIN "botanicals" ON "botanicals"."id" = "gin_botanicals"."botanical_id" WHERE "botanicals"."id" IN (1, 2, 3) AND ("gins"."id" != $1) GROUP BY gins.id HAVING (COUNT(distinct botanicals.id) >= 3)  [["id", 1]]
=> #<ActiveRecord::Relation []>

【讨论】:

  • 有趣!如果我理解的话,这将返回所有(默认情况下)具有与@gin 相同的植物的杜松子酒。那是对的吗?我很好奇 OP 所说的“包含特定植物的杜松子酒列表”是什么意思。
  • @jvillian 完全正确。 “HAVING COUNT(distinct botanicals.id) = x”设置要返回的行需要多少个botanicals.id
  • 已编辑。应该是HAVING COUNT(distinct botanicals.id) &gt;= ? 以获得正确的结果。
  • 好吧,跳到我的娄我的亲爱的!每天学些新东西。 (尤其是在你身边闲逛。)我会试着把一些东西放在缓冲区里。
  • @jvillian 自从我从社区得到这个后,我不能真正接受所有的功劳。 stackoverflow.com/questions/36131803/…
【解决方案3】:

最后解决起来很容易。昨晚有人发布了这个答案,但很快就删除了。

我需要一个简单的连接:

Gin.joins(:botanicals).where("botanicals.name LIKE ?", "%#{@botanical.name}%")

所以现在,当在植物药“当归根”的展示页面上时,我有一个 each 循环,它显示了在其植物药中也列出了“当归根”的任何其他杜松子酒。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-01-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多