对未保存的对象关联关系:通过轨道/ ActiveRecord的的has_many [英] Rails/ActiveRecord has_many through: association on unsaved objects

查看:108
本文介绍了对未保存的对象关联关系:通过轨道/ ActiveRecord的的has_many的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

让我们从这些类工作:

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 用户可以参加许多项目与角色为成员管理​​。该连接模式被称为 ProjectParticipation

A user can participate in many projects with a role as a member or a manager. The connecting model is called ProjectParticipation.

我现在使用的未保存的对象关联的问题。下面的命令的工作方式,我认为他们应该工作:

I now have a problem using the associations on unsaved objects. The following commands work like I think they should work:

# 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 本身,我可以访问项目用户 u.projects

So far so good - AR created the ProjectParticipation by itself and I can access the projects of a user with u.projects.

但是,如果我创建它不能正常工作 ProjectParticipation 由我自己:

But it does not work if I create the ProjectParticipation by myself:

# 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 的项目,如前。

但是,如果我去直接通过参股,该项目显示了:

But if I go through the participations directly, the project shows up:

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

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

Shouldn't it work like the first example directly: u.projects returning me all projects not depending on whether I create the join object by myself or not? Or how can I make AR aware of this?

推荐答案

简短的回答:不,第二个例子是行不通的,它的工作在第一个例子的方式。您必须使用直接与用户和项目对象创建中间协会的第一个例子的方式。

Short answer: No, second example won't work the way it worked in first example. You must use first example's way of creating intermediate associations directly with user and project objects.

龙的答案

在我们开始之前,我们应该知道如何的has_many:通过的ActiveRecord正在处理::基。所以,让我们开始<一个href="https://github.com/rails/rails/blob/02d3a253610eaf9c80587913b366e3fa0f56b71f/activerecord/lib/active_record/associations.rb#L1245-L1248"相对=nofollow> 的has_many(名称,范围=零,选择= {},和放大器;扩展) 方法,调用它的关联<一个href="https://github.com/rails/rails/blob/02d3a253610eaf9c80587913b366e3fa0f56b71f/activerecord/lib/active_record/associations/builder/association.rb#L28-L42"相对=nofollow>建设者此处,在方法返回的<一个结束href="https://github.com/rails/rails/blob/02d3a253610eaf9c80587913b366e3fa0f56b71f/activerecord/lib/active_record/reflection.rb#L623"相对=nofollow>反射然后添加反射到<一个href="https://github.com/rails/rails/blob/02d3a253610eaf9c80587913b366e3fa0f56b71f/activerecord/lib/active_record/reflection.rb#L11"相对=nofollow>散作为缓存<一个href="https://github.com/rails/rails/blob/02d3a253610eaf9c80587913b366e3fa0f56b71f/activerecord/lib/active_record/reflection.rb#L33-L35"相对=nofollow>在这里键 - 值对。

Before we start, we should know how has_many :through is being handled in ActiveRecord::Base. So, let's start with has_many(name, scope = nil, options = {}, &extension) method which calls its association builder here, at the end of method the returned reflection and then add reflection to a hash as a cache with key-value pair here.

现在的问题是,如何做这些协会被激活?!?!

Now question is, how do these associations gets activated?!?!

这是因为<一href="https://github.com/rails/rails/blob/02d3a253610eaf9c80587913b366e3fa0f56b71f/activerecord/lib/active_record/associations.rb#L150-L160"相对=nofollow> 协会(姓名) 方法。这就要求<一href="https://github.com/rails/rails/blob/02d3a253610eaf9c80587913b366e3fa0f56b71f/activerecord/lib/active_record/reflection.rb#L440-L445"相对=nofollow> association_class 方法,它实际上是调用和返回这个常量:<一href="https://github.com/rails/rails/blob/02d3a253610eaf9c80587913b366e3fa0f56b71f/activerecord/lib/active_record/reflection.rb#L442"相对=nofollow> 协会:: HasManyThroughAssociation ,使<一href="https://github.com/rails/rails/blob/02d3a253610eaf9c80587913b366e3fa0f56b71f/activerecord/lib/active_record/associations.rb#L118"相对=nofollow>这一行来自动加载的 active_record /协会/ has_​​many_through_association.rb 和<一href="https://github.com/rails/rails/blob/02d3a253610eaf9c80587913b366e3fa0f56b71f/activerecord/lib/active_record/associations/has_many_through_association.rb#L7-L12"相对=nofollow>实例的实例 <一href="https://github.com/rails/rails/blob/02d3a253610eaf9c80587913b366e3fa0f56b71f/activerecord/lib/active_record/associations.rb#L155"相对=nofollow>此处。这是<一href="https://github.com/rails/rails/blob/02d3a253610eaf9c80587913b366e3fa0f56b71f/activerecord/lib/active_record/associations/association.rb#L27"相对=nofollow>当关联被创建,并在接下来的复位方法被调用它获取在子类中调用的所有者和反思保存的ActiveRecord ::协会:: CollectionAssociation <一个href="https://github.com/rails/rails/blob/02d3a253610eaf9c80587913b366e3fa0f56b71f/activerecord/lib/active_record/associations/collection_association.rb#L64-L67"相对=nofollow>此处。

It's because of association(name) method. Which calls association_class method, which actually calls and return this constant: Associations::HasManyThroughAssociation, that makes this line to autoload active_record/associations/has_many_through_association.rb and instantiate its instance here. This is where owner and reflection are saved when the association is being created and in the next reset method is being called which gets invoked in the subclass ActiveRecord::Associations::CollectionAssociation here.

为什么这个复位电话是重要的?因为,它集 @target 作为数组。这 @target 是所有相关联的对象,当你犯了一个查询保存,然后当您在code再利用作出新的它,而不是用来作为高速缓存阵列查询。这就是为什么叫 user.projects (其中用户和项目坚持分贝,即呼吁:用户= User.find(1)然后 user.projects )将一个数据库查询,并再次调用它不会。

Why this reset call was important? Because, it sets @target as an array. This @target is the array where all associated objects are stored when you make a query and then used as cache when you reuse it in your code instead of making a new query. That's why calling user.projects(where user and projects persists in db, i.e. calling: user = User.find(1) and then user.projects) will make a db query and calling it again won't.

所以,当你犯了一个<一个href="https://github.com/rails/rails/blob/02d3a253610eaf9c80587913b366e3fa0f56b71f/activerecord/lib/active_record/associations/collection_association.rb#L29-L37"相对=nofollow>读者上的关联调用,例如: user.projects ,它<一个href="https://github.com/rails/rails/blob/02d3a253610eaf9c80587913b366e3fa0f56b71f/activerecord/lib/active_record/associations/collection_association.rb#L36"相对=nofollow>调用collectionProxy ,填充在 @target 的<一个href="https://github.com/rails/rails/blob/02d3a253610eaf9c80587913b366e3fa0f56b71f/activerecord/lib/active_record/associations/collection_association.rb#L373-L380"相对=nofollow> load_target

So, when you make a reader call on an association, e.g.: user.projects, it invokes the collectionProxy, before populating the @target from load_target.

这是勉强划伤表面。但是,你怎么协会正在构建使用建设者的想法(这将创建<一个href="https://github.com/rails/rails/blob/02d3a253610eaf9c80587913b366e3fa0f56b71f/activerecord/lib/active_record/reflection.rb#L15-L31"相对=根据条件)和nofollow的>不同的反射在目标变量中读取数据创建代理。

This is barely scratching the surface. But, you get the idea how associations are being build using builders(which creates different reflection based on the condition) and creates proxies for reading data in the target variable.

你的第一例和第二例之间的区别是他们的协会的建设者正在为创建关联的反思<一个调用的方法href="https://github.com/rails/rails/blob/02d3a253610eaf9c80587913b366e3fa0f56b71f/activerecord/lib/active_record/reflection.rb#L15-L31"相对=nofollow>(基于宏),代理和目标实例变量。

The difference between your first and second examples is the way their association builders are being invoked for creating associations' reflection(based on macro), proxy and target instance variables.

第一个例子:

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 无代理,它只是<一href="https://github.com/rails/rails/blob/02d3a253610eaf9c80587913b366e3fa0f56b71f/activerecord/lib/active_record/associations/association.rb#L27"相对=nofollow>目标和所有者。

但是,如果你真的倾向于让你的第二个例子的工作,你只需要做到这一点:

However, if you are really inclined to make your second example work, you will just have to do this:

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

和现在:

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

在这是创建/保存协会的一个非常糟糕的方式我的意见。因此,坚持第一个例子本身。

In my opinion which is a very bad way of creating/saving associations. So, stick with the first example itself.

这篇关于对未保存的对象关联关系:通过轨道/ ActiveRecord的的has_many的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆