具有嵌套关联条件检查的has_many范围

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了具有嵌套关联条件检查的has_many范围相关的知识,希望对你有一定的参考价值。

我有一个具有许多项目的帐户模型,以及一个具有许多要求的项目模型,一个帐户充当库帐户,其他帐户为用户帐户。

需求表具有一个external_id,它是一个字符串ID,用于与外部数据库相关联,并且在帐户的上下文中是唯一的。但是库帐户和用户帐户中的需求可以共享相同的external_id(即“ REQ-23”),因此它不是“需求”表上的唯一键。

有一个RelatedRequirement表,该表映射了相关的需求。

下面,我想通过在has_many范围内进行一个account_id检查来获得属于用户帐户的相关要求。

数据库端:

account
  id

project
  id
  account_id

requirement
  id
  project_id
  external_id

related_requirements
   external_id
   related_external_id

模型侧:

class RelatedRequirement < ActiveRecord::Base
  belongs_to :requirement,,
    foreign_key: :external_id,
    class_name: "Requirement",
    primary_key: :external_id

  belongs_to :related_requirement,
    foreign_key: :related_external_id,
    class_name: "Requirement",
    primary_key: :external_id
end

class Requirement < ActiveRecord::Base
 belongs_to :project
  has_one :account, through: :project

  has_many :related_requirement_mappings,
    foreign_key: :external_id,
    class_name: "RelatedRequirement",
    primary_key: :external_id

  has_many :related_requirements,
    # === this works ===
    #-> { joins(:project).where(projects: {account_id: 2 }) },
    # === this doesn't work ===
    -> { joins(:project).where(projects: {account_id: project.account_id }) },
    through: :related_requirement_mappings, 
    foreign_key: :related_external_id, 
    class_name: "Requirement",
    primary_key: :external_id

我的问题:

  1. 主要问题,我希望能够使用上面的has_many:related_requirements在同一个帐户内获取related_requirements,但是我无法进入我范围内的关联account_id,
>req = Requirement.find(1)
>req.related_requirements  

  Requirement Load (81.3ms)  SELECT "requirements".* FROM "requirements"
NameError (undefined local variable or method `project' for #<Requirement::ActiveRecord_Relation:0x00007fe3f0b1ac20>)

但是我目前能做的是:

class Requirement < ActiveRecord::Base

has_many :related_requirements,
    #-> { joins(:project).where(prjects: {account_id: project.account_id }) },
    through: :related_requirement_mappings, 
    foreign_key: :related_external_id, 
    class_name: "Requirement",
    primary_key: :external_id

scope :for_account, -> (account) { joins(:project).where(projects: {account_id: account.id}) }
>req = Requirement.find(1)
>req.related_requirements.for_account(req.account)

这看起来有点愚蠢,因为看起来我在req上调用实例方法,而且我应该能够从related_requirements内部访问req的帐户,而不必定义此额外的for_account范围。

  1. 由于external_id不是唯一字段,所以RelatedRequirement表中的belongs_to需求实际上可以映射到多个需求,但只返回一个。理想情况下,我想添加一个范围条件(基于另一个克隆的数据库字段),以表明它来自库帐户。但是,如果我这样做,则上面的related_requirements查询将中断,因为范围条件将包含在查询中,并且不再返回用户帐户的related_requirements,我对此很感兴趣。当我没有明确调用它时,我不确定我在RelatedRequirement范围中指定的条件如何应用于我为需求定义的范围。
class RelatedRequirement < ActiveRecord::Base
  belongs_to :requirement,
    -> { where cloned: nil },
    foreign_key: :external_id,
    class_name: "Requirement",
    primary_key: :external_id

  belongs_to :related_requirement,
    -> { where cloned: nil },
    foreign_key: :related_external_id,
    class_name: "Requirement",
    primary_key: :external_id


class Requirement < ActiveRecord::Base
  belongs_to :project
  has_one :account, through: :project

  has_many :related_requirement_mappings,
    foreign_key: :external_id,
    class_name: "RelatedRequirement",
    primary_key: :external_id

  has_many :related_requirements,
    # -> { joins(:project).where(projects: {account_id: project.account_id }) },
    through: :related_requirement_mappings, 
    foreign_key: :related_external_id, 
    class_name: "Requirement",
    primary_key: :external_id

  scope :for_account, -> (account) { joins(:project).where(projects: {account_id: account.id}) }

irb(main):060:0> r.related_requirements.to_sql
=> "SELECT "requirements".* FROM "requirements" INNER JOIN "related_requirements" ON "requirements"."external_id" = "related_requirements"."related_external_id" WHERE "related_requirements"."external_id" = 'REQ-1' AND "requirements"."cloned" IS NULL"

irb(main):061:0> r.related_requirements.for_account(r.account)
=> #<ActiveRecord::AssociationRelation []>

答案

我找到了自己的问题的答案,发布以供将来参考。

如果我在has_many关系中使用参数定义范围,则该参数将是self对象,但是我不需要使用该参数调用has_many。它会隐式传递。

has_many :related_requirements,
    (requirement) -> { joins(:project).where(projects: {account_id: requirement.account_id }) },
    through: :related_requirement_mappings, 
    foreign_key: :related_external_id, 
    class_name: "Requirement",
    primary_key: :external_id

然后,我就可以根据属于该帐户的条件(这是一个嵌套的关联)来确定相关需求:

irb(main):028:0> r.related_requirements.to_sql
  Account Load (6.0ms)  SELECT  "accounts".* FROM "accounts" INNER JOIN "projects" ON "accounts"."id" = "projects"."account_id" WHERE "projects"."id" = $1 LIMIT 1  [["id", 9]]
=> "SELECT "requirements".* FROM "requirements" INNER JOIN "projects" ON "projects"."id" = "requirements"."project_id" INNER JOIN "related_requirements" ON "requirements"."external_id" = "related_requirements"."related_external_id" WHERE "related_requirements"."external_id" = 'REQ-1' AND "projects"."account_id" = 2"

以上是关于具有嵌套关联条件检查的has_many范围的主要内容,如果未能解决你的问题,请参考以下文章

Rails has_many,如何实现嵌套关联?

在 has_many 上具有条件的 CakePHP 分页

Has_Many 通过关联和嵌套属性

包括与条件的左连接 has_many 关联

如何将参数传递给 Rails 4 中的 has_many 关联范围?

预加载 has_many 与动态条件的关联