【问题标题】:has_many scope with a condition check on a nested associationhas_many 范围,对嵌套关联进行条件检查
【发布时间】:2019-10-18 21:23:29
【问题描述】:

我有一个账户模型,有很多项目,还有一个项目模型,有很多需求。一个账户作为图书馆账户,其他账户是用户账户。

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

有一个 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 范围中指定的条件是如何应用到我为 Requirement 定义的范围中的,而我没有明确调用它。
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 []>

【问题讨论】:

    标签: ruby-on-rails activerecord


    【解决方案1】:

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

    如果我在 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"
    

    【讨论】:

      猜你喜欢
      • 2014-08-08
      • 2016-07-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-05-23
      • 1970-01-01
      • 1970-01-01
      • 2023-03-10
      相关资源
      最近更新 更多