gpt4 book ai didi

ruby-on-rails - Rails/ActiveRecord has_many 到 : association on unsaved objects

转载 作者:行者123 更新时间:2023-12-03 16:54:17 26 4
gpt4 key购买 nike

让我们使用这些类:

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

一个 user可以参加很多 projects角色为 membermanager .连接模型称为 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我自己可以访问 projectsuseru.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 .所以,让我们从 has_many(name, scope = nil, options = {}, &extension) 开始吧。调用其关联的方法builder here , 在方法结束时返回 reflection然后将反射添加到 hash作为缓存 with key-value pair here .
现在的问题是,这些关联是如何被激活的?!?!
这是因为 association(name) 方法。调用 association_class 方法,它实际调用并返回这个常量: Associations::HasManyThroughAssociation , 这使得 this line自动加载 active_record/associations/has_many_through_association.rb 和 instantiate its instance here .这是owner and reflection在创建关联和调用下一个重置方法时保存,该方法在子类 ActiveRecord::Associations::CollectionAssociation 中调用here .
为什么这个重置电话很重要?因为,它设置了 @target作为一个数组。这个@target是在您进行查询时存储所有关联对象的数组,然后在您在代码中重用它而不是进行新查询时用作缓存。这就是为什么调用 user.projects (其中用户和项目在数据库中持续存在,即调用: user = User.find(1) 然后 user.projects )将进行数据库查询并且再次调用它不会。
因此,当您制作 reader调用关联,例如:user.projects , 它invokes the collectionProxy , 在填充 @target 之前来自 load_target .
这只是触及表面而已。但是,您会了解如何使用构建器(根据条件创建 different reflection)构建关联并创建代理以读取目标变量中的数据。
tl;博士
您的第一个和第二个示例之间的区别在于它们的关联构建器被调用以创建关联的反射 (based on macro) ,代理和目标实例变量。
第一个例子:

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 没有代理, 它只有 target and owner .
但是,如果您真的倾向于使您的第二个示例工作,您只需要这样做:
u.association(:projects).instance_variable_set('@target', [p])
现在:
u.projects
#=> #<ActiveRecord::Associations::CollectionProxy [#<Project id: nil, name: nil, created_at: nil, updated_at: nil>]>
在我看来,这是一种非常糟糕的创建/保存关联的方式。所以,坚持第一个例子本身。

关于ruby-on-rails - Rails/ActiveRecord has_many 到 : association on unsaved objects,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26651811/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com