Ответ 1
Вы можете передать объект в область has_many как параметр для лямбда
has_many :report_cards, -> (student) { ... }
Вот пример:
Пусть говорит, что у меня есть объект Student, который имеет has_many отношения с объектами ReportCard. Объекты ReportCard имеют логическое поле, называемое "градуированным", эти флаги, которые они были оценены. Так выглядит:
class Student < ActiveRecord
has_many :report_cards
end
class ReportCard < ActiveRecord
# graded :boolean (comes from DB)
belongs_to :student
end
Теперь предположим, что вы хотите создать область по умолчанию, чтобы, если у ученика нет градуированных ReportCards, вы хотите увидеть все из них, но если у них есть хотя бы одна градуированная ReportCard, вы хотите видеть только градуированные, Наконец, скажем, вы заказываете их с помощью "semester_number".
Использование этой области в ReportCard работает правильно:
scope :only_graded_if_possible, ->(student) { where(graded: true, student: student).order(:semester_number).presence || order(:semester_number) }
Но я хочу, чтобы это была область по умолчанию для Student, поэтому я попытался:
class Student < ActiveRecord
has_many :report_cards, ->{ where(graded: true).order(:semester_number).presence || order(:semester_number) }
end
но это не работает. Он не будет возвращать никакие report_cards, если в целом db есть одна градуированная отчетная карточка. Рассматривая запущенные запросы, сначала он запускает что-то вроде:
SELECT report_cards.* FROM report_cards WHERE reports_cards.graded = t ORDER BY semester_number ASC
Я думаю, что это должно быть настоящее? проверьте часть запроса на присутствие и обратите внимание, что он не фильтрует на Студент вообще! Поэтому, если есть одна масштабируемая версия report_card, проверка проходит, а затем выполняется следующий запрос, чтобы получить возвращаемое значение:
SELECT report_cards.* FROM reports_card WHERE report_card.student_id = 'student id here' AND report_card.graded = t ORDER BY semester_number
Этот запрос действительно был бы правильным, если бы у ученика была оцененная карта отчета, но она всегда пуста, если он этого не делает.
Я предполагаю, что после этого добавляется фильтрация на Student. Поэтому я попытался как-то заставить его отфильтровать ученика с места в карьер:
has_many :report_cards, ->{ where(graded: true, student: self).order(:semester_number).presence || order(:semester_number) }
Это не работает либо потому, что кажется, что "я" в этой области не является объектом Student, как я предполагал, но является списком всех идентификаторов report_card. Ниже приведен запрос:
SELECT report_cards.* FROM report_cards WHERE report_cards.graded = t AND report_cards.student_id IN (SELECT report_cards.id FROM report_cards) ORDER BY semester_number ASC
Это даже близко не подходит. Как я могу заставить это работать?
Я думаю, что это действительно сводится к тому, что кто-то может передать "я" (что означает текущий объект Student) в качестве параметра в область, применяемую в "has_many". Возможно, это невозможно.
Вы можете передать объект в область has_many как параметр для лямбда
has_many :report_cards, -> (student) { ... }
Попробуйте следующее:
class Student < ActiveRecord::Base
has_many :report_cards, ->{ where(graded: true).order(:semester_number).presence || unscoped.order(:semester_number) }
end
Я использую это в своем проекте, где у меня есть модель, связанная с пользователями:
has_many :users, -> { only_deleted }
И в модели Users создайте область only_deleted, возвращая ваши удаленные пользователи.