2009-04-25 14 views
5

En mi tienda en línea, un pedido está listo para enviarse si está en el estado "autorizado" y no tiene ningún envío asociado. En este momento estoy haciendo esto:Buscar todos los objetos sin objetos has_many asociados

class Order < ActiveRecord::Base 
    has_many :shipments, :dependent => :destroy 

    def self.ready_to_ship 
     unshipped_orders = Array.new 
     Order.all(:conditions => 'state = "authorized"', :include => :shipments).each do |o| 
      unshipped_orders << o if o.shipments.empty? 
     end 
     unshipped_orders 
    end 
end 

¿Hay una manera mejor?

Respuesta

8

También puede consulta en la asociación con el hallazgo de sintaxis normal:

Order.find(:all, :include => "shipments", :conditions => ["orders.state = ? AND shipments.id IS NULL", "authorized"]) 
+0

Esto no funcionará. Como es una asociación has_many, la clave externa se almacena en la tabla de envíos. –

+0

Es por eso que nos unimos en el identificador NULL –

+0

Correcto, mi mal (chupo en SQL). De todos modos, esto funciona, excepto que tienes que reemplazar "estado" por "estado" (para mi caso particular) y agregar un "y" antes de "envíos.id". Haz esos cambios, y marcaré esta como respuesta. –

1

Una opción es poner una cuenta_envío en el pedido, donde se actualizará automáticamente con la cantidad de envíos que le anexe. A continuación, sólo

Order.all(:conditions => [:state => "authorized", :shipment_count => 0]) 

Como alternativa, puede ensuciarse las manos con un poco de SQL:

Order.find_by_sql("SELECT * FROM 
    (SELECT orders.*, count(shipments) AS shipment_count FROM orders 
    LEFT JOIN shipments ON orders.id = shipments.order_id 
    WHERE orders.status = 'authorized' GROUP BY orders.id) 
    AS order WHERE shipment_count = 0") 

prueba que antes de utilizarlo, tal como SQL no es exactamente mi bolsa, pero creo que está cerca a derecha. Lo hice funcionar para arreglos similares de objetos en mi DB de producción, que es MySQL.

Tenga en cuenta que si no tiene un índice en orders.status, se lo recomendaría encarecidamente.

Qué hace la consulta: la subconsulta capta todos los recuentos de pedidos para todos los pedidos que están en estado autorizado. La consulta externa filtra esa lista solo a las que tienen recuentos de envío igual a cero.

Probablemente hay otra forma en que podría hacerlo, un poco contraintuitiva:

"SELECT DISTINCT orders.* FROM orders 
    LEFT JOIN shipments ON orders.id = shipments.order_id 
    WHERE orders.status = 'authorized' AND shipments.id IS NULL" 

agarrar todas las órdenes que están autorizados y no tengan una entrada en la tabla de envíos;)

+0

Su sintaxis es falsa, debería ser: conditions => {: state => "authorized",: shipment_count => 0} ... en otras palabras, use un hash, no una matriz. –

12

en Rails 3 utilizando AREL

Order.includes('shipments').where(['orders.state = ?', 'authorized']).where('shipments.id IS NULL') 
Cuestiones relacionadas