【问题标题】:Can I access the calling object on an active record association in a has-and-belongs-to-many relationship?我可以在拥有和属于多的关系中访问活动记录关联上的调用对象吗?
【发布时间】:2018-03-25 13:53:08
【问题描述】:

如果我们有这样的has_and_belongs_to_many 关系:

class Assembly < ApplicationRecord
  has_and_belongs_to_many :parts
end

class Part < ApplicationRecord
  has_and_belongs_to_many :assemblies
end

我们使用关联来查找属于Assemblyid1 的部件,如下所示:

Assembly.find(1).parts

那么有没有办法引用调用关联对象的对象——在这种情况下,有一种方法可以从 Part 引用特定的 Assembly 吗?像这样的:

Assembly.find(1).parts.each do |part|

  the_calling_assembly = part.assembly_that_referenced_me()

end

而且,没有内置的方法来做到这一点,关于如何创建这种行为的任何建议?

【问题讨论】:

  • 如果您只是将Assemby.find(1) 分配给一个变量,那么您的问题就解决了,不是吗?

标签: ruby-on-rails ruby activerecord associations rails-activerecord


【解决方案1】:

这只是一个想法

如果你去http://api.rubyonrails.org/并选择Active Record/Associations你会看到有2个类

1)Collection Proxies

2)Class Methods

但你也可以看到Association 的所有类和模块,但我相信你应该修补Collection Proxies class

因此您在 lib/core_ext/active_record/associations/collection_proxy.rb 中创建类,并在服务器启动时为 required 创建一个初始化程序。

如果这是你想要达到的目标

Assembly.find(1).parts.each do |part|

  the_calling_assembly = part.assembly_that_referenced_me()

end

您可以创建一个 method 来完成它。在 github 上检查 CollectionProxy 类是个好主意

module ActiveRecord
  module Associations
    class CollectionProxy < Relation
       def parent_instance
           Assembly.find(self.assembly_id)
       end
    end
  end
end

所以这不是一个完整的答案,可能我没有意识到您的功能的复杂性,但我建议您降低复杂性。

还要了解assembly_part 中的class 是什么,因为那是您需要修补的那个。 (assembly_part.class)

【讨论】:

  • 感谢您教我收集代理。经过一番搜索,找到了Extending has_many association proxy,这似乎是我想要达到的目标
  • @cwq 您正在使用一个块,以使用该方法进行迭代。基本上,您使用 Assembly.first.parts 调用方法 has_many ,但是通过使用块,您可以在调用 Assembly.first.parts {|parent| puts parent } 中传递一段代码。该块将执行以下 def parts; yield parts.parent; end,yield 将在 iterate 之间methodblock。它将返回parts.parent{|parent| // your code},然后block 将返回print parent。我也是块新手,但它们是 rails 最棒的功能,你的问题真的很棒
  • @cwq 来自 rails 的源代码包含有关如何使用该类 association proxy 传递块 github.com/rails/rails/blob/… 的更多信息,通过在该代码中搜索 block 可以找到更多信息。我不是 100% 确定我要发布什么
【解决方案2】:

实现这一点的最佳方法是对用于实现多对多关系的连接表使用连接模型。

所以,现在应该有一张桌子,assemblies_parts

你应该写一个模型:

#assemblies_part.rb

class AssemblyPart < ApplicationRecord
    belongs_to :assembly
    belongs_to :part
end

你也应该改变你的其他模型

class Assembly < ApplicationRecord
    has_many :assembly_parts
    has_many :parts, through: :assembly_parts
end

class Part < ApplicationRecord
    has_many :assembly_parts
    has_many :assemblies, through: :assembly_parts
end

所以,你现在可以这样做了;

Assembly.find(1).assembly_parts.each do |assembly_part|
     assembly = assembly_part.assembly
     part = assembly_part.part
end

如果您想将程序集持久化到零件,在整个过程中,您可以执行以下操作(注意:这不会持久化到数据库)

class Part < ApplicationRecord
    #...

    attr_accessor :assembly

end

然后执行以下操作:

Assembly.find(1).assembly_parts.each do |assembly_part|
     part = assembly_part.part
     part.assembly = assembly_part.assembly #only valid through the current process
end

通过使用attr_accessor,您也可以在没有连接模型n 实现的情况下执行此操作。

上述解决方案不需要新的迁移。由于我是在手机屏幕上执行此操作,因此可能存在轻微的拼写错误或语法错误。 无论如何,我希望它会有所帮助。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多