Рекомендуется избегать N + 1 при перечислении объектов, полученных методом в связанном объекте - PullRequest
1 голос
/ 01 июня 2019

Я пытаюсь найти лучший способ исправить проблему N + 1 в моем приложении rails.

Допустим, у меня есть модель Student, которая имеет many ассоциацию terms.

student = Student.find_by_name('John')
student.terms 
# [{term: 1}, {term: 2}, {term: 3}]

Итак, мне нужно иметь возможность отображать список студенческих терминов, но я также должен показывать предыдущий термин в том же виде. В настоящее время для этого у меня есть метод, который получает предыдущий термин по атрибуту start_at, который является значением DateTime, эквивалентным первому дню срока.

# Term.rb
def previous_term
  Term.where("start_at < ?", start_at).order(start_at: :desc).first&.start_at
end

Но использование этого метода в списке терминов делает запрос для каждого термина для каждого учащегося

- @terms.each do |term|
  %td= term.start_at # list current term start_at
  %td= term.previous_term # show previous term start_at - N+1 is here

Я думал о загрузке всех условий студента в контроллер и создании метода класса в модели Term, этот метод будет обнаруживать предыдущий термин из упорядоченного набора терминов.

# Term.rb
def self.get_previous(term_collection, term_id)
  term_collection.detect do |term|
    # logic to filter and get the previous term
  end
end

Таким образом, это будет фильтрация объектов в памяти вместо создания соединений с базой данных, но я знаю, что есть лучший и более умный способ сделать это (возможно, лучшая практика?).

Ответы [ 2 ]

2 голосов
/ 01 июня 2019

Из ваших заказанных терминов student.terms.order(start_at: :desc) вы можете создать массив ruby ​​loop with index на ваш взгляд

- @terms.each_with_index do |term, index|
  %td= term.start_at
  - if index > 0
    %td= @terms[0..index-1].pluck(:name)

Надеюсь, это поможет

0 голосов
/ 01 июня 2019

Измените определение previous_term на

def previous_term
  where("start_at < ?", start_at).order(start_at: :desc).first&.start_at
end

, чтобы вы могли вызвать

student.terms.previous_term


Альтернатива в Ruby, так как у вас уже естьколлекция terms = student.terms, используйте Enumerable # max_by (или min_by).Что-то вроде:
previous_term = terms.max_by { |term| term.start_at } # or terms.max_by(&:start_at)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...