through rails many left inner has_and_belongs_to_many has foreign example create belongs and ruby-on-rails ruby join has-many-through

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:

  1. includes cargas asociadas registro (s); En este caso los registros de Availability . joins no cargan ningún registro asociado. Por lo tanto, debe usar las includes cuando desee usar los datos del modelo de unión, por ejemplo, el price visualización en algún lugar. Por otro lado, las joins deben usarse si pretende utilizar los datos del modelo de combinación solo en la consulta, por ejemplo, en las cláusulas ORDER BY o WHERE .

  2. includes cargas todos los registros, mientras que las joins 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 que Home.joins(:availabilities) cargaría solo aquellas casas que hayan asociado al menos una disponibilidad.

También vea esta pregunta .