2015-02-23 5 views
4

Скажем, у меня есть отношения в Rails к таблице, которая использует STI нравится:Rails 4.2: Нетерпеливый заряжания has_many связь с ИППП

class Vehicle < ActiveRecord::Base; end 

class Car < Vehicle; end 

class Truck < Vehicle; end 

class Person < ActiveRecord::Base 
    has_many :cars 
    has_many :trucks 
    has_many :vehicles 
end 

... и я хочу, чтобы загрузить Личность и все ее автомобилей и грузовиков в одном запросе. Это не работает:

# Generates three queries 
p = Person.includes([:cars, trucks]).first 

... и это близко, но не повезло здесь:

# Preloads vehicles in one query 
p = Person.includes(:vehicles).first 
# and this has the correct class (Car or Truck) 
p.vehicles.first 
# but this still runs another query 
p.cars 

я не мог сделать что-то подобное в person.rb:

def cars 
    vehicles.find_all { |v| v.is_a? Car } 
end 

, но затем Person#cars больше не является прокси-сервером коллекции, а я , как.

Есть ли элегантное решение?

EDIT: добавление этого объекта дает мне элементы, которые я хочу в массивах, с одним запросом; это действительно очень близко к тому, что я хочу:

def vehicle_hash 
    @vehicle_hash ||= vehicles.group_by {|v| 
    v.type.tableize 
    } 
end 

%w(cars trucks).each do |assoc| 
    define_method "#{assoc}_from_hash".to_sym do 
    vehicle_hash[assoc] || [] 
    end 
end 

и теперь я могу сделать Person.first.cars_from_hash (или найти лучшее название для моей несинтетической прецеденту).

ответ

2

Когда вы используете includes, он сохраняет эти загруженные записи в association_cache, которые вы можете посмотреть в консоли. Когда вы делаете p = Person.includes(:vehicles), он сохраняет эти записи как ассоциацию под ключом :vehicles. Он использует любой ключ, который вы передаете ему в составе.

Поэтому, когда вы звоните p.cars, он замечает, что у него нет ключа :cars в association_cache, и он должен пойти посмотреть их. Он не понимает, что автомобили смешиваются с ключом :vehicles.

Для доступа к кэшированным автомобилям либо через p.vehicles ИЛИ p.cars потребуется их кеширование под обоими этими ключами.

И что он хранит, это не просто массив - это отношение. Таким образом, вы не можете просто вручную хранить записи в Hash.

Из предлагаемых вами решений, я думаю, что включение каждого ключа, вероятно, является простейшим кодом. Person.includes(:cars, :trucks) 3 SQL-инструкции не так уж плохи, если вы делаете это только один раз за запрос.

Если производительность является проблемой, я думаю, что самое простое решение было бы очень похоже на то, что вы предложили. Вероятно, я бы написал новый метод find_all_cars вместо того, чтобы переписать метод отношений.

Хотя, я бы, вероятно, перезаписать vehicles и позволить ему принять аргумент типа:

def vehicles(sti_type=nil) 
    return super unless sti_type 
    super.find_all { |v| v.type == sti_type } 
end 

EDIT

Вы можете получить vehicles кэшируются Rails, так что вы, вероятно, можете просто полагаться на это ,Ваш define_method s может также сделать:

%w(cars trucks).each do |assoc| 
    define_method "preloaded_#{assoc}" do 
    klass = self.class.reflect_on_all_associations.detect { |assn| assn.name.to_s == assoc }.klass 
    vehicles.select { |a| a.is_a? klass } 
    end 
end 

Даже если вы не используете includes, первый раз, когда вы называете это, это будет кэшировать-ассоциацию, потому что вы select ING, не where Ing. Конечно, вы все равно не получите отношения.

Это не очень мило, но мне нравится, что он содержится в одном методе, который не зависит от других.

+0

Да, ударный удар может быть заметным; Я делаю это для шести классов ИППП у каждого из двух родителей. Хм ... что бы было связано с созданием фактических отношений для assocation_cache? – Nate

+0

Я добавил вариант вашего решения вверх. Смутно разумно? Любопытно, однако: зачем вам менять транспортные средства, чтобы взять тип STI? (В моем случае я обычно загружаю все свои типы STI для каждого запроса.) – Nate

+0

Выглядит так же хорошо, как и любой другой. Хотя в результате вы все еще не получаете отношения. Я искал решение, которое добавляло наименьшие методы и наименьшую степень сложности. Ваше решение, кажется, добавляет слишком много для моего вкуса. Я также добавлю изменение. – evanbikes