Rails/ActiveRecord has_many through:未保存对象的关联

2024-04-12

让我们使用这些类:

class User < ActiveRecord::Base
    has_many :project_participations
    has_many :projects, through: :project_participations, inverse_of: :users
end

class ProjectParticipation < ActiveRecord::Base
    belongs_to :user
    belongs_to :project

    enum role: { member: 0, manager: 1 }
end

class Project < ActiveRecord::Base
    has_many :project_participations
    has_many :users, through: :project_participations, inverse_of: :projects
end

A user可以参加很多projects的角色为member or a manager。连接模型称为ProjectParticipation.

我现在在使用未保存对象上的关联时遇到问题。以下命令的工作方式就像我认为的那样:

# first example

u = User.new
p = Project.new

u.projects << p

u.projects
=> #<ActiveRecord::Associations::CollectionProxy [#<Project id: nil>]>

u.project_participations
=> #<ActiveRecord::Associations::CollectionProxy [#<ProjectParticipation id: nil, user_id: nil, project_id: nil, role: nil>]>

到目前为止一切顺利 - AR 创造了ProjectParticipation本身,我可以访问projects of a user with u.projects.

但如果我创建它就不起作用ProjectParticipation我自己:

# second example

u = User.new
pp = ProjectParticipation.new
p = Project.new

pp.project = p # assign project to project_participation

u.project_participations << pp # assign project_participation to user

u.project_participations
=> #<ActiveRecord::Associations::CollectionProxy [#<ProjectParticipation id: nil, user_id: nil, project_id: nil, role: nil>]>

u.projects
=> #<ActiveRecord::Associations::CollectionProxy []>

为什么项目是空的?我无法通过以下方式访问这些项目u.projects像以前一样。

但如果我直接查看参与,该项目就会显示:

u.project_participations.map(&:project)
=> [#<Project id: nil>]

它不应该像第一个例子一样直接工作吗:u.projects返回我所有项目不取决于我是否自己创建连接对象?或者我怎样才能让 AR 意识到这一点?


简短回答:不,第二个示例不会像第一个示例那样工作。您必须使用第一个示例的方式直接创建与用户和项目对象的中间关联。

长答案:

在开始之前,我们应该知道如何has_many :through正在处理中ActiveRecord::Base。那么,让我们从调用其关联的方法建设者在这里 https://github.com/rails/rails/blob/02d3a253610eaf9c80587913b366e3fa0f56b71f/activerecord/lib/active_record/associations/builder/association.rb#L28-L42,在方法结束时返回反射 https://github.com/rails/rails/blob/02d3a253610eaf9c80587913b366e3fa0f56b71f/activerecord/lib/active_record/reflection.rb#L623然后添加反射hash https://github.com/rails/rails/blob/02d3a253610eaf9c80587913b366e3fa0f56b71f/activerecord/lib/active_record/reflection.rb#L11作为缓存这里有键值对 https://github.com/rails/rails/blob/02d3a253610eaf9c80587913b366e3fa0f56b71f/activerecord/lib/active_record/reflection.rb#L33-L35.

现在的问题是,这些关联如何被激活?!?!

这是因为association(name) https://github.com/rails/rails/blob/02d3a253610eaf9c80587913b366e3fa0f56b71f/activerecord/lib/active_record/associations.rb#L150-L160方法。哪个调用association_class https://github.com/rails/rails/blob/02d3a253610eaf9c80587913b366e3fa0f56b71f/activerecord/lib/active_record/reflection.rb#L440-L445方法,它实际上调用并返回这个常量:Associations::HasManyThroughAssociation https://github.com/rails/rails/blob/02d3a253610eaf9c80587913b366e3fa0f56b71f/activerecord/lib/active_record/reflection.rb#L442,这使得这条线 https://github.com/rails/rails/blob/02d3a253610eaf9c80587913b366e3fa0f56b71f/activerecord/lib/active_record/associations.rb#L118自动加载active_record/associations/has_many_through_association.rb and 实例化它的实例 https://github.com/rails/rails/blob/02d3a253610eaf9c80587913b366e3fa0f56b71f/activerecord/lib/active_record/associations/has_many_through_association.rb#L7-L12 here https://github.com/rails/rails/blob/02d3a253610eaf9c80587913b366e3fa0f56b71f/activerecord/lib/active_record/associations.rb#L155。这是哪里所有者和反思 https://github.com/rails/rails/blob/02d3a253610eaf9c80587913b366e3fa0f56b71f/activerecord/lib/active_record/associations/association.rb#L27在创建关联以及调用下一个在子类中调用的重置方法时保存ActiveRecord::Associations::CollectionAssociation here https://github.com/rails/rails/blob/02d3a253610eaf9c80587913b366e3fa0f56b71f/activerecord/lib/active_record/associations/collection_association.rb#L64-L67.

为什么这个重置调用很重要?因为,它设定了@target作为一个数组。这@target是在进行查询时存储所有关联对象的数组,然后在代码中重用它而不是进行新查询时将其用作缓存。这就是为什么打电话user.projects(其中用户和项目保留在数据库中,即调用:user = User.find(1)进而user.projects)将进行数据库查询并再次调用它不会。

所以,当你制作一个reader https://github.com/rails/rails/blob/02d3a253610eaf9c80587913b366e3fa0f56b71f/activerecord/lib/active_record/associations/collection_association.rb#L29-L37呼吁协会,例如:user.projects, it 调用集合代理 https://github.com/rails/rails/blob/02d3a253610eaf9c80587913b366e3fa0f56b71f/activerecord/lib/active_record/associations/collection_association.rb#L36,在填充之前@target from load_target https://github.com/rails/rails/blob/02d3a253610eaf9c80587913b366e3fa0f56b71f/activerecord/lib/active_record/associations/collection_association.rb#L373-L380.

这还只是触及表面。但是,您知道如何使用构建器构建关联(这会创建不同的反映 https://github.com/rails/rails/blob/02d3a253610eaf9c80587913b366e3fa0f56b71f/activerecord/lib/active_record/reflection.rb#L15-L31基于条件)并创建用于读取目标变量中的数据的代理。

tl;dr

第一个和第二个示例之间的区别在于调用它们的关联构建器来创建关联反射的方式(基于宏) https://github.com/rails/rails/blob/02d3a253610eaf9c80587913b366e3fa0f56b71f/activerecord/lib/active_record/reflection.rb#L15-L31、代理和目标实例变量。

第一个例子:

u = User.new
p = Project.new
u.projects << p

u.association(:projects)
#=> ActiveRecord::Associations::HasManyThroughAssociation object
#=> @proxy = #<ActiveRecord::Associations::CollectionProxy [#<Project id: nil, name: nil, created_at: nil, updated_at: nil>]>
#=> @target = [#<Project id: nil, name: nil, created_at: nil, updated_at: nil>]

u.association(:project_participations)
#=> ActiveRecord::Associations::HasManyAssociation object
#=> @proxy = #<ActiveRecord::Associations::CollectionProxy [#<ProjectParticipation id: nil, user_id: nil, project_id: nil, role: nil, created_at: nil, updated_at: nil>]>
#=> @target = [#<ProjectParticipation id: nil, user_id: nil, project_id: nil, role: nil, created_at: nil, updated_at: nil>]

u.project_participations.first.association(:project)
#=> ActiveRecord::Associations::BelongsToAssociation object
#=> @target = #<Project id: nil, name: nil, created_at: nil, updated_at: nil>

第二个例子:

u = User.new
pp = ProjectParticipation.new
p = Project.new

pp.project = p # assign project to project_participation

u.project_participations << pp # assign project_participation to user

u.association(:projects)
#=> ActiveRecord::Associations::HasManyThroughAssociation object
#=> @proxy = nil
#=> @target = []

u.association(:project_participations)
#=> ActiveRecord::Associations::HasManyAssociation object
#=> @proxy = #<ActiveRecord::Associations::CollectionProxy [#<ProjectParticipation id: nil, user_id: nil, project_id: nil, role: nil, created_at: nil, updated_at: nil>
#=> @target = [#<ProjectParticipation id: nil, user_id: nil, project_id: nil, role: nil, created_at: nil, updated_at: nil>]

u.project_participations.first.association(:project)
#=> ActiveRecord::Associations::BelongsToAssociation object
#=> @target = #<Project id: nil, name: nil, created_at: nil, updated_at: nil>

没有代理BelongsToAssociation,它刚刚目标和所有者 https://github.com/rails/rails/blob/02d3a253610eaf9c80587913b366e3fa0f56b71f/activerecord/lib/active_record/associations/association.rb#L27.

但是,如果您确实想让第二个示例发挥作用,则只需执行以下操作:

u.association(:projects).instance_variable_set('@target', [p])

And now:

u.projects
#=>  #<ActiveRecord::Associations::CollectionProxy [#<Project id: nil, name: nil, created_at: nil, updated_at: nil>]>

在我看来,这是创建/保存关联的一种非常糟糕的方式。所以,坚持第一个例子本身。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Rails/ActiveRecord has_many through:未保存对象的关联 的相关文章

随机推荐

  • 有什么方法可以在 Jenkins 中获取 Gitlab 管道工件吗?

    我在gitlab中有一个项目 该项目会在存储库中的每次签入时构建 并在 gitlab 管道成功时创建构建工件 我想在我的詹金斯管道工作中获得这些构建工件 有什么办法可以做到这一点吗 我在 Jenkins 中找不到任何插件来执行此操作 任何帮
  • setInterval 不起作用(Javascript)

    我正在尝试使用 JavaScript 中的 setInterval 定期重绘画布 但是 当我调用 setInterval 函数时 我传递给它的函数只运行一次 这是我的代码的简化版本
  • 无法打开下载保存对话框

    使用下面的代码我无法显示打开 另存为文件对话框 public void ProcessRequest HttpContext context string link context Request QueryString Link stri
  • 在 google app engine python 中设置 cron 作业

    我刚刚开始使用 Google App Engine 所以我仍在学习如何配置所有内容 我编写了一个名为 parsexml py 的脚本 我希望每 10 分钟左右运行一次 该文件位于我的主目录中 与 main py app yaml 等一起 据
  • 自动添加或删除 Woocommerce 购物车中的免费产品

    我正在尝试创建代码 一旦客户达到购物车中的特定价格点 该代码就会自动将商品添加到客户的购物车中 如果他们只订购虚拟产品 我试图排除这种情况的发生 因为 免费礼物 仅适用于正在发货的产品 我使用的代码是以正确的金额添加免费礼物 但并不排除任何
  • 如何以 4/6/7 角度传递表单提交上的所有选中复选框值

    我想在组件中获取表单的所有已检查项目而不使用change or click 功能 因为它无法获取已检查的项目 这是我在 TS 中的数组 PartyRoles Id 1 Name Vendor Checked true Id 2 Name C
  • VSCode 中的 Flutter 初始化

    我一直在使用 VS Code 开发一个 flutter 项目 当我今天打开我的项目时 有一条通知显示 正在初始化 flutter 这可能需要几分钟 然后就被击中了 此外 flutter run 和 flutter doctor 等所有命令都
  • 将 WKNSURLRequest 崩溃为?其他类型

    当我尝试强制转换 WKNSURLRequest 以及其他类 全部来自 WebKit 框架 时 我遇到了严重崩溃 例如在游乐场 import UIKit import WebKit final class Sigh NSObject NSCl
  • bash set -e and i=0;让i++不同意

    仅当变量的先前值为零时 以下带有调试选项 set e v 的脚本才会在增量运算符处失败 bin bash set e v i 1 let i echo I am still here i 0 let i echo I am still he
  • 在 Flask 中使用 root_path 参数

    我尝试遵循一个教程 该教程旨在演示如何更改静态和模板文件夹在根目录中的位置 但是我无法让这个例子工作 应用程序运行正常 但在查找样式表 GET static style css HTTP 1 1 404 时返回 404 因此 它似乎可以找到
  • 如何从字符串形式的发送者向模拟器发送短信

    我经常在手机中收到短信 其中发送者中包含一些字符串而不是数字 例如公司名称 我想测试一些对这些短信做出反应的应用程序 但是如何将这样的短信发送到模拟器 如果我运行模拟器并执行以下操作 远程登录本地主机 5554 短信发送 MyBank 这是
  • 如何在 Lattice 包中将标签添加到 Levelplot 的顶部 X 轴

    所以我正在使用类似于此的 levelplot 制作类似相关矩阵的图 取自 将相关矩阵绘制成图表 https stackoverflow com questions 5453336 plot correlation matrix into a
  • 如何使带有前导零的不连续字符数字序列连续?

    我有这个字符向量 dput t line c 0304 0305 0306 0308 0311 0313 0314 0316 0318 0321 0322 0323 0324 0326 0327 0330 0333 0337 0338 03
  • 从服务器序列故障转移加载 .js?

    让我们想象一个网页需要加载一个 javascript 文件 即my js 是否可以组织以下故障转移加载顺序 如果服务器 A 已启动 则加载my js来自服务器A 否则 如果服务器 B 已启动 则加载my js来自服务器 B 否则 如果服务器
  • 使用maven仓库作为本地ivy缓存

    是否有可能使用本地 Maven 存储库 m2 作为本地 Ivy 缓存 ivy 他们有不同的布局 有时我使用 Maven 有时我使用 SBT 它在下面使用 Ivy 所以我在 Maven 和 Ivy 中都有相同库的 2 个副本 我想使用相同的目
  • iPhone 上 UIView 和 UILabels 上的渐变[重复]

    这个问题在这里已经有答案了 可能的重复 在 iPhone 应用程序中手动绘制渐变 https stackoverflow com questions 227005 manually drawing a gradient in iphone
  • 如何在函数内使用 ls() 搜索环境?

    我想找到一组函数并保存它们 因为我想将它们以 Rdata 文件发送到远程服务器 并且我不想在服务器上安装新的包 尽管我使用下面的方法遇到错误 但更简单 更好的方法是值得欢迎的 MWE 这是两个虚拟函数 abcd fun 1 lt funct
  • @Column columnDefinition 使哪些属性变得多余?

    我经常指定我的 Column像这样的注释 Column columnDefinition character varying 100 not null length 100 nullable false 正如你所看到的 我指定length
  • MySQL - 如何根据输入的长/纬度选择经度和纬度以逗号分隔的行?

    好的 我的数据库表中有以下两列 其中包含以下长 纬度 longitude 2 2426305000000184 0 7077123000000256 latitude 53 4807593 51 5459269 到目前为止我有这个查询 fu
  • Rails/ActiveRecord has_many through:未保存对象的关联

    让我们使用这些类 class User lt ActiveRecord Base has many project participations has many projects through project participation