Rails 4 - как предоставить имена псевдонимов include() и join() в активном запросе записей
Как я могу присвоить псевдоним, например. includes()
?
Приводится следующее:
- Пользователь: активная модель записи
- Студент: активная модель записи, наследует пользователя (STI)
- Учитель: активная модель записи, наследует пользователя (STI)
- Проект: модель активной записи
Вот несколько примеров:
FIRST CASE (больше ассоциаций STI)
Project.all.includes(:students, :teachers).order('teachers_projects.name ASC') # order on teachers
Project.all.includes(:students, :teachers).order('users.name ASC') # order on students
Rails автоматически использует псевдоним teachers_projects
для :teachers
в SQL. Как я могу перезаписать это, чтобы я мог использовать псевдоним teachers
вместо teachers_projects
в SQL? :students
получает псевдоним users
.
Эти примеры не выполняются:
Project.all.includes(:students, :teachers).order('teachers.name ASC')
Project.all.includes(:students, :teachers).order('students.name ASC')
Project.all.includes(:students, :teachers).order('students_projects.name ASC')
SECOND CASE (одна ассоциация STI)
Если в методе includes()
используется только :students
(без :teachers
), Rails использует псевдоним имени базового класса STI users
(без _projects
) для :students
:
Project.all.includes(:students).order('users.name ASC') # order on students
Эти примеры не выполняются:
Project.all.includes(:students).order('students.name ASC')
Project.all.includes(:students).order('students_projects.name ASC')
Вопрос
Возможно, что-то вроде:
Project.all.includes(:students).alias(students: :my_alias)
RAILS ALIAS TRACKER
https://github.com/rails/rails/blob/v4.2.0/activerecord/lib/active_record/associations/alias_tracker.rb#L59
ТЕСТИРОВАНИЕ APP
https://gist.github.com/phlegx/add77d24ebc57f211e8b
https://github.com/phlegx/rails_query_alias_names
Ответы
Ответ 1
У Arel действительно есть метод псевдонима.
student_alias = Student.arel_table.alias(:my_student_table_alias)
caveat: это потребует от вас использования еще большего количества рук Arel и сделайте соединение вручную. И соединения в Arel могут немного усложниться, если вы не привыкли к ним.
student_alias_join = student_alias.create_on(
Project.arel_table[:primary_key].eq(student_alias[:project_id])
)
Project.joins(
Project.arel_table.create_join(
student_alias, student_alias_join, Arel::Nodes::OuterJoin
)
).order(...)
Что-то вроде этого должно это сделать.
Разумеется, для этого метода класса с :my_student_table_alias
этот параметр сделает его более аккуратным и многоразовым, поскольку это будет выглядеть немного беспорядочным в контроллере.
Ответ 2
Я собираюсь сделать еще один подход к этой проблеме: вместо того, чтобы пытаться управлять именами псевдонимов в ваших запросах с помощью метода .alias
, я разрешу Rails/Arel обрабатывать это и просто посмотреть правильное имя таблицы ( с псевдонимом или без него), когда это необходимо в пределах области действия.
Добавьте этот вспомогательный метод к своей модели, который вы сможете вызвать из области, чтобы узнать, используется ли область в JOIN
, которая имеет псевдонимы таблицы (несколько объединений в одной таблице) или, с другой стороны, область не имеет псевдонимов для имени таблицы.
def self.current_table_name
current_table = current_scope.arel.source.left
case current_table
when Arel::Table
current_table.name
when Arel::Nodes::TableAlias
current_table.right
else
fail
end
end
В качестве базового объекта для поиска таблицы isl используется current_scope
. Я вызываю source
на этом объекте, чтобы получить Arel::SelectManager
, который, в свою очередь, даст вам текущую таблицу на #left
, Есть два варианта: либо у вас есть Arel::Table
(нет псевдонима, имя таблицы на #name
), либо у вас есть Arel::Nodes::TableAlias
с псевдонимом на #right
.
Теперь вам нужно использовать это только в своих операторах order
(untested):
Project.all.includes(:students, :teachers).order("#{current_table_name}.name ASC")
Project.all.includes(:students, :teachers).order("#{current_table_name}.name ASC")
Project.all.includes(:students, :teachers).order("#{current_table_name}.name ASC")
Похожие вопросы: