Rails 4 Dostęp Do Atrybutów Join Table
Mam has_many through
join table setup for a recipe app where Ingredient
and Meal
connect through MealIngredient
. W obrębie MealIngredient
, mam meal_id
, ingredient_id
, i amount
. Moje pytanie brzmi: jak Mogę uzyskać dostęp do kolumny amount
?
W moim widoku przepisu, przeglądam składniki:
@meal.ingredients.each do |i|
Mogę uzyskać dostęp do Właściwości składnika, ale nie do ilości z MealIngredient
rekordu przyłączenia.
Próbowałem użyć includes
W zapytaniu robi @meal.ingredients.includes(:meal_ingredients)
, ale nie jestem pewien, jak uzyskać dostęp do amount
w ramach wspomnianej pętli. Kiedy używam i.inspect
, nie widzę żadnych odniesień do tabeli meal_ingredients
.
Czy Jest jakiś sposób, aby uzyskać dostęp do zmiennej w tej pętli za pomocą i.amount
?
Z góry dziękuję za wszelką pomoc!
2 answers
Zaktualizowano dnia 08/01/2020-RPECK
Zmagaliśmy się z tym miesiącami, dopóki nie znaleźliśmy odpowiedniego rozwiązania:
--
Rozszerzenia Stowarzyszenia ActiveRecord
Problem polega na tym, że Rails po prostu użyje foreign_keys
w tabeli Dołącz, aby załadować potrzebne dane asocjacyjne. Jeśli model join nie zostanie załadowany bezpośrednio, nie będzie on miał dostępu do atrybutów join
Niektóre żerowania prowadzą nas do ActiveRecord Association Extensions
- sposób dostępu do danych pośredniczących pomiędzy różnymi asocjacjami ActiveRecord (za pomocą zbioru o nazwie proxy_association
). Pozwoli to na dostęp do dodatkowych atrybutów z modelu join, dołączając je do "oryginalnego" modelu:
#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
To powinno teraz dołączyć atrybut amount
do obiektów ingredient
, pozwalając na wykonanie:
@meal = Meal.find 1
@meal.ingredients.each do |ingredient|
ingredient.amount
end
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-01-08 11:35:24
W tym przypadku należy wykonać pętlę przez skojarzenie meal_ingredients
. Powinieneś wczytać asocjację ingredients
, aby zmniejszyć zapytania db.
@meal.meal_ingredients.includes(:ingredient).each do |meal_ingredient|
puts meal_ingredient.amount
puts meal_ingredient.ingredient.name
end
UPDATE
Ta aktualizacja pojawiła się po odpowiedzi Rich ' a Pecka, ale myślę, że jest prostszy sposób, aby osiągnąć to, co zrobił.
@meal.ingredients.select('ingredients.*, meal_ingredients.amount').each do |ingredient|
puts ingredient.amount
puts ingredient.name
end
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-08-11 16:36:15