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:
includescargas asociadas registro (s); En este caso los registros deAvailability.joinsno cargan ningún registro asociado. Por lo tanto, debe usar lasincludescuando desee usar los datos del modelo de unión, por ejemplo, elpricevisualización en algún lugar. Por otro lado, lasjoinsdeben usarse si pretende utilizar los datos del modelo de combinación solo en la consulta, por ejemplo, en las cláusulasORDER BYoWHERE.includescargas todos los registros, mientras que lasjoinssolo 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 .