简体   繁体   中英

Can I access the calling object on an active record association in a has-and-belongs-to-many relationship?

If we have a has_and_belongs_to_many relationship like this:

class Assembly < ApplicationRecord
  has_and_belongs_to_many :parts
end

class Part < ApplicationRecord
  has_and_belongs_to_many :assemblies
end

And we use an association to find parts belonging to an Assembly with id of 1 like this:

Assembly.find(1).parts

Then is there a way to reference the object that called associated objects - in this case, a way to reference the specific Assembly from the Part ? Something like this:

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

  the_calling_assembly = part.assembly_that_referenced_me()

end

And, of there is not an builtin way to do this, any suggestions on how to create this behavior?

The best way to achieve this is by using a join model for the join table that is used to implement the many-to-many relation.

So, by now there should be a table, assemblies_parts

You should write a model:

#assemblies_part.rb

class AssemblyPart < ApplicationRecord
    belongs_to :assembly
    belongs_to :part
end

And you should change your other models too

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

So, you could now do;

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

If you would like to persist the assembly to the part, throughout the process, you could do the following (caution: this does not persist to the DB)

class Part < ApplicationRecord
    #...

    attr_accessor :assembly

end

And do this next:

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

By using the attr_accessor you can also do this without the join model n implementation.

No new migrations are required for the above solution. Minor typos or syntax errors might exist because I'm doing this on a phone screen. I hope it helps, anyhow.

this is just an idea

If you go to http://api.rubyonrails.org/ and select Active Record/Associations you will see that there are 2 classes

1) Collection Proxies

2) Class Methods

but also you can see al the classes and modules for the Association , but I believe you should patch the Collection Proxies class

so you create the class in lib/core_ext/active_record/associations/collection_proxy.rb and you create an initializer to required at your server startup.

if this is what you want to achieve

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

  the_calling_assembly = part.assembly_that_referenced_me()

end

you can just create a method that does it. It is a good idea to check the CollectionProxy class on github

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

so this is not a complete answer and probably I did not realize the complexity of your functionalities, but I suggest you to decrease the complexity.

Also understand what is the class from assembly_part , because that is the one you need to patch. ( assembly_part.class )

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM