ruby-on-rails Rails包含条件

krcsximq  于 2023-02-26  发布在  Ruby
关注(0)|答案(9)|浏览(186)

在Rails〉3.2中是否可以向includes方法生成的连接语句添加条件?
假设我有两个模型,Person和Note,每个人都有很多条注解,每条注解属于一个人,每条注解都有一个属性important
我想找到所有的人只预加载重要的笔记。在SQL中,这将是:

SELECT *
FROM people
LEFT JOIN notes ON notes.person_id = people.id AND notes.important = 't'

在Rails中,唯一类似的方法是使用includes(注意:joins不会预加载注解),如下所示:

Person.includes(:notes).where(:important, true)

但是,这将生成以下SQL查询,该查询返回不同的结果集:

SELECT *
FROM people
LEFT JOIN notes ON notes.person_id = people.id
WHERE notes.important = 't'

请注意,第一个结果集包括所有人,第二个结果集只包括与重要笔记相关的人。
还要注意:从Python 3.1开始,条件不再使用。

s4chpxco

s4chpxco1#

根据本指南进行活动记录查询
您可以像下面这样为包含指定条件,以便立即加载

Person.includes(:notes).where("notes.important", true)

无论如何,建议使用joins
此问题的解决方法是创建另一个类似的关联

class Person < ActiveRecord::Base
  has_many :important_notes, :class_name => 'Note', 
           :conditions => ['important = ?', true]
end

然后您就可以执行此操作

Person.find(:all, include: :important_notes)
2ic8powd

2ic8powd2#

Rails 5+语法:

Person.includes(:notes).where(notes: {important: true})

嵌套:

Person.includes(notes: [:grades]).where(notes: {important: true, grades: {important: true})
rjzwgtxy

rjzwgtxy3#

导轨4.2+:

选项A -“预加载”:多重选择,使用“id IN(...)"

class Person < ActiveRecord::Base
  has_many :notes
  has_many :important_notes, -> { where(important: true) }, class_name: "Note"
end

Person.preload(:important_notes)

SQL语句:

SELECT "people".* FROM "people"

SELECT "notes".* FROM "notes" WHERE "notes"."important" = ? AND "notes"."person_id" IN (1, 2)

选项B -“急切加载”:一个巨大的选择,使用“左连接”

class Person < ActiveRecord::Base
  has_many :notes
  has_many :important_notes, -> { where(important: true) }, class_name: "Note"
end

Person.eager_load(:important_notes)

SQL语句:

SELECT "people"."id" AS t0_r0, "people"."name" AS t0_r1, "people"."created_at" AS t0_r2, "people"."updated_at" AS t0_r3, "notes"."id" AS t1_r0, "notes"."person_id" AS t1_r1, "notes"."important" AS t1_r2 
FROM "people" 
LEFT OUTER JOIN "notes" ON "notes"."person_id" = "people"."id" AND "notes"."important" = ?
7xzttuei

7xzttuei4#

日语stackoverflow中也讨论了同样的问题。相当笨拙,但是下面的代码似乎可以工作,至少在rail5上是这样。

Person.eager_load(:notes).joins("AND notes.important = 't'")

一个重要的方面是,通过这种方式,你可以写任意的连接条件。缺点是,你不能使用占位符,所以你需要小心使用参数作为连接条件。
https://ja.stackoverflow.com/q/22812/754

5gfr0r5j

5gfr0r5j5#

我无法使用的条件包括像利奥科雷亚的答案。Insted我需要用途:

Lead.includes(:contacts).where("contacts.primary" =>true).first

或者你也可以

Lead.includes(:contacts).where("contacts.primary" =>true).find(8877)

最后一项将检索ID为8877的销售线索,但仅包括其主要联系人

lbsnaicq

lbsnaicq6#

对于感兴趣的人,我在记录属性为false的情况下进行了尝试

Lead.includes(:contacts).where("contacts.primary" => false).first

但这并不起作用,因为对于布尔型,只有true起作用,所以我将其改为包含where.not

Lead.includes(:contacts).where.not("contacts.primary" => true).first

这个很好用

rqcrx0a6

rqcrx0a67#

一个非常简单的例子:

User.includes(:posts).where(posts: { name: 'John' })

注:

  • 如果您的关联是has_one,请将:posts替换为:post(注意复数/单数)。
  • 更多信息here

进一步联想一下,如果你有

thing.other_things.includes(:even_more_things).where(other_things: {col1: 'value'})

但是你想在even_more_things上过滤,你可以使用:

thing.other_things.joins(:even_more_things).where(even_more_things: { attribute: value })
sy5wg1nm

sy5wg1nm8#

一种方法是使用连接自己编写LEFT JOIN子句:

Person.joins('LEFT JOIN "notes" ON "notes"."person_id" = "people.id" AND "notes"."important" IS "t"')

不过,不太好看。

ppcbkaq5

ppcbkaq59#

你可以试试

Person.includes(:notes).where(notes: { important: 't' })

确保Note表名为notes,并亲自对关联进行建模,如下所示:has_many:备注,或者您可以更改它人员。includes(:关联)。where(table_name:{重要:'t '})

相关问题