于 2020 年 8 月 1 日更新 - RPECK
为此苦苦挣扎了几个月,直到我们找到了合适的解决方案:
--
ActiveRecord Association Extensions
您遇到的问题是 Rails 只会使用连接表中的 foreign_keys 来加载您需要的关联数据。除非您实际直接加载连接模型,否则它不会让您能够访问连接属性
一些搜索将我们引向ActiveRecord Association Extensions - 一种访问不同 ActiveRecord 关联之间的中间数据的方法(使用称为 proxy_association 的集合)。这将允许您从连接模型访问额外的属性,将它们附加到您的“原始”模型:
#app/models/ingredient.rb
class Ingredient < ActiveRecord::Base
attr_accessor :amount #-> need a setter/getter
end
#app/models/meal.rb
class Meal < ActiveRecord::Base
has_many :meal_ingredients
has_many :ingredients, { -> extending: IngredientAmount }, through: :meal_ingredients
end
#app/models/concerns/ingerdient_amount.rb
module IngredientAmount
# => Load
# => Triggered whenever module is invoked (allows us to populate response data)
# => #<Method: ActiveRecord::Relation#load(&block) c:/Dev/Apps/pwinty-integration/.bundle/ruby/2.7.0/gems/activerecord-6.0.2.1/lib/active_record/relation.rb:614>
def load(&block)
# => This is called from the following reference - if you want to dig deeper, you can use method(:exec_queries).source_location
# => c:/Dev/Apps/pwinty-integration/.bundle/ruby/2.7.0/gems/activerecord-6.0.2.1/lib/active_record/association_relation.rb:42
unless loaded?
exec_queries do |record|
record.assign_attributes({amount: items[record.id]}) if items[record.id].present?
end
end
end
# Load
# Deprecated with AR 6.0
#def load
# amounts.each do |amount|
# proxy_association.target << amount
# end
#end
#Private
private
#Amounts
# Not needed after AR 6.0
#def amounts
# return_array = []
# through_collection.each_with_index do |through,i|
# associate = through.send(reflection_name)
# associate.assign_attributes({amount: items[i]}) if items[i].present?
# return_array.concat Array.new(1).fill( associate )
# end
# return_array
#end
#######################
# Variables #
#######################
#Association
def reflection_name
proxy_association.source_reflection.name
end
#Foreign Key
def through_source_key
proxy_association.reflection.source_reflection.foreign_key
end
#Primary Key
def through_primary_key
proxy_association.reflection.through_reflection.active_record_primary_key
end
#Through Name
def through_name
proxy_association.reflection.through_reflection.name
end
#Through
def through_collection
proxy_association.owner.send through_name
end
#Captions
def items
#through_collection.map(&:amount)
through_collection.pluck(through_source_key, :amount).map{ |id, amount| { id => amount } }.inject(:merge) #-> { id: record, id: record }
end
#Target
# This no longer works with AR 6.0+
#def target_collection
# #load_target
# proxy_association.target
#end
end
这应该现在将amount 属性附加到您的ingredient 对象,允许您执行:
@meal = Meal.find 1
@meal.ingredients.each do |ingredient|
ingredient.amount
end