ruby on rails - rails - Rieles-Ordenar por datos de la tabla de unión
rails 5 many to many (5)
Tengo un proyecto RoR en las obras. Aquí están las secciones aplicables de mis modelos.
Casa
has_many :communities, :through => :availabilities
has_many :availabilities, :order => "price ASC"
Comunidad
has_many :homes, :through => :availabilities
has_many :availabilities
Disponibilidad
belongs_to :home
belongs_to :community
La tabla de "disponibilidades" en la base de datos tiene la columna de datos adicionales "precio"
Así que ahora puedo llamar
@home.availabilities.each do |a|
a.community.name
a.price
y recuperar los datos de disponibilidad ordenados por precio que yo quiera. Mi pregunta es la siguiente:
¿Hay una manera de ordenar Casas por avaliabilities.first.price
(primero = más bajo)? Tal vez algo con default_scope :order
?
En Rails 5.2+, puede recibir una advertencia de desaprobación al pasar un parámetro de cadena al método de pedido:
ADVERTENCIA DE DEPRECACIÓN: Método de consulta peligroso (método cuyos argumentos se utilizan como SQL sin formato) llamado con argumento (s) sin atributos: "table.column". Los argumentos que no son de atributo se rechazarán en Rails 6.0. Este método no debe llamarse con valores proporcionados por el usuario, como parámetros de solicitud o atributos de modelo.
Para resolver esto, puedes usar Arel.sql()
:
scope :ordered, -> {
includes(:availabilities).order(Arel.sql(''availabilities.price''))
}
Lo descubrí con la ayuda de este post relacionado .
Trasladé el pedido fuera del modelo de Casa al modelo de Disponibilidad:
Disponibilidad
default_scope :order => "price ASC"
Luego, me interesé por cargar las disponibilidades en el modelo de Casa y ordenarlas por precio:
Casa
default_scope :include => :availabilities, :order => "availabilities.price ASC"
Otra forma de lograr esto:
scope :ordered, -> { includes(:availabilities).order(Availability.arel_table[:price]) }
También puede especificar la dirección ASC
con
scope :ordered, -> { includes(:availabilities).order(Availability.arel_table[:price].asc) }
DESC
:
scope :ordered, -> { includes(:availabilities).order(Availability.arel_table[:price].desc) }
El uso de arel_table
en el modelo ActiveRecord
hace guardar contra el escenario cuando se cambia el nombre de la tabla (pero esto ocurre muy raramente).
Tenga en cuenta que es bueno agregar main_table#id
para una clasificación determinada.
Así que la versión final sería:
scope :ordered, -> {
includes(:availabilities).
order(Availability.arel_table[:price].asc, order(Home.arel_table[:id].asc)
}
Yo sugeriría evitar el uso de default_scope
, especialmente en algo como el precio en otra tabla. Cada vez que utilice esa tabla, se realizarán las combinaciones y los pedidos, lo que posiblemente dará resultados extraños en consultas complejas y, de todos modos, hará que su consulta sea más lenta.
No hay nada de malo en un ámbito propio, es más sencillo y, aún más claro, puede hacerlo tan simple como:
scope :ordered, -> { includes(:availabilities).order(''availabilities.price'') }
PD: Recuerda añadir un índice en el price
answer @ecoológica:
scope :ordered, -> { includes(:availabilities).order(''availabilities.price'') }
es genial, pero debe mencionarse que includes
podría, y en algunos casos debe ser reemplazado por joins
. Ambos tienen sus casos de uso óptimo .
Desde el punto de vista práctico hay dos diferencias principales:
includes
cargas asociadas registro (s); En este caso los registros deAvailability
.joins
no cargan ningún registro asociado. Por lo tanto, debe usar lasincludes
cuando desee usar los datos del modelo de unión, por ejemplo, elprice
visualización en algún lugar. Por otro lado, lasjoins
deben usarse si pretende utilizar los datos del modelo de combinación solo en la consulta, por ejemplo, en las cláusulasORDER BY
oWHERE
.includes
cargas todos los registros, mientras que lasjoins
solo cargan los registros que tienen asociado el modelo de combinación. Entonces, en el caso de OP,Home.includes(:availabilities)
cargaría todas las casas, mientras queHome.joins(:availabilities)
cargaría solo aquellas casas que hayan asociado al menos una disponibilidad.
También vea esta pregunta .