【问题标题】:Rails find all records except already second level associatedRails 查找除已关联的第二级之外的所有记录
【发布时间】:2019-01-08 09:04:20
【问题描述】:

我的 Rails 应用程序中有以下模型

型号community

class Community < ActiveRecord::Base
  has_many :community_program
end

型号community_program

class CommunityProgram < ActiveRecord::Base
  belongs_to :program_name
end

型号program_name

class ProgramName < ActiveRecord::Base
  has_many :community_programs
end

我正在尝试找到一种方法来提取所有不属于community_programprogram_names,而community_program 已经分配给community

我已经能够拉出所有program_names,然后删除已经与community 关联的program_names,但这非常昂贵。可以直接拉取二级不关联记录吗?

编辑:这个问题与链接的问题不同,因为在这种情况下,program_name 可能与不同社区的社区计划有关联,但如果没有,我们仍然希望包含该名称与当前community 的关联。

**编辑 2:我想提取所有尚未分配给与特定社区关联的社区计划的程序名称。它们可以与与不同社区相关联的社区计划相关联。用例是用户应该能够从剩余名称列表中选择一个名称,并将其分配给与其社区相关的社区计划。

计划名称是全球性的,尽管特定社区的社区计划的计划名称是唯一的,但不同的社区可以将社区计划与相同的计划名称相关联**

【问题讨论】:

  • @JoshBrody 我不认为这是重复的,因为他们可以有其他关联我只是想确保他们不通过社区计划与特定社区相关联,但可以将同名连接到不同的社区
  • 你能重新表述一下你需要做什么吗?我理解这一点:您需要所有 ProgramName 1)不与任何 CommunityProgram 关联和 2)已经与 Community 关联??
  • 顺便说一句,我认为您在 CommunityProgram 模型中缺少一个关联 belongs_to :community
  • @gasc 是的,您是对的,缺少关联。我在移动设备上使用 Stackoverflow,所以我无法编辑它,但应该有关联

标签: ruby-on-rails ruby ruby-on-rails-4 activerecord


【解决方案1】:

except_community 是一个特定的Community,应该排除哪些关联。下面的代码将产生一个 SQL 查询:

except_relation = except_community.
  community_programs.
  select(:program_name_id)

ProgramName.
  where.not(id: except_relation)

您可以将其添加为Community模型的实例方法。

【讨论】:

  • 如果可能的话,您能否解释一下为什么在这种情况下selectpluck 更好,尽管pluck 通常应该更有效?
  • pluck 通常不应该更有效:) select 不是 Enumerable#select 而是 ActiveRecord::Relation 的可链接方法,如 whereorder 等。所以, except_relation = ... 不会产生真正的 SQL 查询(如 pluck),而只是构建 ActiveRecord::Relation 的实例。所以最后几行代码看起来像Model.where.not(id: ar_relation),它产生一个像SELECT * FROM program_names WHERE id NOT IN(SELECT program_name_id FROM ...)这样的SQL查询
【解决方案2】:

可能是这样的:

class Community < ActiveRecord::Base
  has_many :community_programs
end

class CommunityProgram < ActiveRecord::Base
  belongs_to :program_name, optional: true
  belongs_to :community
end

class ProgramName < ActiveRecord::Base
  has_many :community_programs
end

some_community            = Community.find some_community_id
programs_from_a_community = some_community.community_programs.map &:id 
program_names_to_exclude  = CommunityProgram.where(id: programs_from_a_community)
                                            .where.not(program_name_id: nil)
                                            .map { |c_p| c_p.program_name.id }
program_names             = ProgramName.where.not(id: program_names_to_exclude)

【讨论】:

    【解决方案3】:

    我之前接受的答案的版本被拒绝,因此我将其添加为另一个答案。 Pavel Mikhailyuk 提供的解决方案解决了我的问题。

    因为我想找到最有效的查询,所以我最终更新它并使用:

    ProgramName.where.not(id: except_community.community_programs.pluck(:program_name_id))
    

    编辑:选择的答案比这更有效。

    【讨论】:

    • 感谢您的编辑建议,我已经通过语法修复更新了我的答案。
    • 你的假设是错误的:select 在我的回答中没有构建任何 ActiveRecord 模型实例,只是 ActiveRecord 关系。我的示例仅生成一个带有 SUB-select 的 sql 查询,而 pluck 一个生成两个 sql 查询并取决于 program_name_id 计数。所以,我的回答“比pluck 更有效” :)
    • @PavelMikhailyuk 是的,你是对的。我的推理在这里有缺陷,运行to_sql 和基准测试清楚地表明select 在这种情况下更有效。
    【解决方案4】:

    您可以通过几种不同的方式执行此操作,具体取决于您在哪个控制器/视图上显示它们。

    在社区模型上创建association

    has_many :unassigned_program_names, -> { where community_program_id: nil }
    

    在 ProgramName 模型上创建一个像这样的scope

    scope :unassigned, -> { where(community_program_id: nil) }
    

    希望这会有所帮助。 :)

    【讨论】:

    • community_program 属于program_name,所以我认为我无法创建您上面描述的范围。
    猜你喜欢
    • 2012-03-26
    • 1970-01-01
    • 2018-04-01
    • 2013-09-24
    • 1970-01-01
    • 1970-01-01
    • 2014-12-08
    • 2011-10-23
    • 1970-01-01
    相关资源
    最近更新 更多