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. они оба имеют свои оптимальные варианты использования.

С практической точки зрения есть два основных отличия:

  1. includes загружает связанные записи; в этом случае Availability записей. joins Не загружайте связанные записи. Поэтому вы должны использовать includes если вы хотите использовать данные из модели join, например display price куда-то. С другой стороны,--2--> следует использовать, если вы собираетесь использовать данные модели соединения только в запросе, например, в ORDER BY или WHERE положения.

  2. 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)
}