Rails-сортировка по данным таблицы соединений
у меня есть проект RoR в работе. Вот применимые разделы моих моделей.
дома
has_many :communities, :through => :availabilities
has_many :availabilities, :order => "price ASC"
сообщество
has_many :homes, :through => :availabilities
has_many :availabilities
в наличии
belongs_to :home
belongs_to :community
таблица "доступность" в базе данных имеет дополнительный столбец данных "цена"
так что теперь я могу позвонить
@home.availabilities.each do |a|
a.community.name
a.price
и получить обратно данные о доступности, упорядоченные по цене, как я хотеть. Мой вопрос таков:--7-->
есть ли способ автоматически заказать дома по avaliabilities.first.price
(первая = самая низкая)? Может быть, что-то с default_scope :order
?
5 ответов
Я бы предложил не использовать default_scope
, особенно на что-то вроде цены на другом столе. Каждый раз, когда вы будете использовать эту таблицу, соединение и порядок будут иметь место, возможно, давая странные результаты в сложных запросах и в любом случае делая ваш запрос медленнее.
нет ничего плохого в собственной области, это проще и еще яснее, вы можете сделать это так же просто, как:
scope :ordered, -> { includes(:availabilities).order('availabilities.price') }
PS: Не забудьте добавить индекс на price
понял это с помощью это связано в должности.
я переместил заказ из домашней модели в модель доступности:
в наличии
default_scope :order => "price ASC"
затем я нетерпеливо загрузил доступные в домашнюю модель и отсортировал по цене:
дома
default_scope :include => :availabilities, :order => "availabilities.price ASC"
@ecoologic ответ:
scope :ordered, -> { includes(:availabilities).order('availabilities.price') }
отлично, но следует упомянуть, что includes
может, а в некоторых случаях должен быть заменен на joins
. они оба имеют свои оптимальные варианты использования.
С практической точки зрения есть два основных отличия:
includes
загружает связанные записи; в этом случаеAvailability
записей.joins
Не загружайте связанные записи. Поэтому вы должны использоватьincludes
если вы хотите использовать данные из модели join, например displayprice
куда-то. С другой стороны,--2--> следует использовать, если вы собираетесь использовать данные модели соединения только в запросе, например, вORDER BY
илиWHERE
положения.includes
загружает все записи, в то время какjoins
загружает только те записи, которые связаны с моделью соединения. Так что в случае ОП,Home.includes(:availabilities)
загрузил бы все дома, покаHome.joins(:availabilities)
будет загружать только те дома, которые связаны хотя бы с одним доступность.
см. Также этот вопрос.
в Rails 5.2+ вы можете получить предупреждение об устаревании при передаче строки param для метода order:
предупреждение об устаревании: Опасный метод запроса(метод, аргументы которого используются как необработанный SQL), вызываемый с аргументом (аргументами) без атрибута: "таблица.столбец." Аргументы без атрибутов будут запрещены в Rails 6.0. Этот метод не должен вызываться с предоставленными пользователем значениями, такими как параметры запроса или атрибуты модели.
чтобы решить эту проблему, вы можете использовать Arel.sql()
:
scope :ordered, -> {
includes(:availabilities).order(Arel.sql('availabilities.price'))
}
еще один способ добиться этого:
scope :ordered, -> { includes(:availabilities).order(Availability.arel_table[:price]) }
вы также можете указать ASC
направление
scope :ordered, -> { includes(:availabilities).order(Availability.arel_table[:price].asc) }
DESC
:
scope :ordered, -> { includes(:availabilities).order(Availability.arel_table[:price].desc) }
используя arel_table
on ActiveRecord
модель позволяет сохранить против сценария при изменении имени таблицы (но это происходит очень редко).
обратите внимание, что приятно добавить main_table#id
для определения сортировки.
таким образом, окончательная версия будет:
scope :ordered, -> {
includes(:availabilities).
order(Availability.arel_table[:price].asc, order(Home.arel_table[:id].asc)
}