2011-03-02 41 views
13

Estoy tratando de SQL obtener de AREL, pero no funciona en el caso uso average(:stars):¿Cómo se usa `to_sql` en AREL cuando se usa` average() `?

Esto funciona:

Review.where("reviewed_user_id = ?", self.reviewed_user_id).to_sql 
#=> "SELECT `reviews`.* FROM `reviews` WHERE (reviewed_user_id = 3)" 

Esto hace que NoMethodError:

Review.where("reviewed_user_id = ?", self.reviewed_user_id).average(:stars).to_sql 
#=> undefined method `to_sql' for 3:Fixnum 

Para que significa que se llama al to_sql sobre el resultado del AREL en lugar de sobre el objeto AREL, pero ¿por qué?

¿Cómo obtener el SQL generado?

Respuesta

24

La razón por la que esto sucede es porque el método promedio está en ActiveRecord::Relation, no en Arel, lo que obliga al cálculo.

m = Review.where('id = ?', 42).method(:average) 
#=> #<Method: ActiveRecord::Relation(ActiveRecord::Calculations)#average> 
m.source_location # or m.__file__ if you're on a different version of Ruby 
#=> ["/Users/jtran/.rvm/gems/ruby-1.9.2-p0/gems/activerecord-3.0.4/lib/active_record/relation/calculations.rb", 65] 

Por el control de la parte interna de ActiveRecord::Calculations, puede derivar cómo conseguir en el SQL que se utiliza.

my_reviewed_user_id = 42 
relation = Review.where('reviewed_user_id = ?', my_reviewed_user_id) 
column = Arel::Attribute.new(Review.unscoped.table, :stars) 
relation.select_values = [column.average] 
relation.to_sql 
#=> "SELECT AVG(\"reviews\".\"stars\") AS avg_id FROM \"reviews\" WHERE (reviewed_user_id = 42)" 

Cuidado si está trabajando en la consola. ActiveRecord::Relation guarda en caché las cosas así que si escribe lo anterior en la consola línea por línea, en realidad no funcionará, porque la impresión bonita fuerza la relación. Sin embargo, la separación de lo anterior con punto y coma y sin líneas nuevas funcionará.

Alternativamente, se puede utilizar directamente Arel, así:

my_reviewed_user_id = 42 
reviews = Arel::Table.new(:reviews) 
reviews.where(reviews[:reviewed_user_id].eq(my_reviewed_user_id)).project(reviews[:stars].average).to_sql 
#=> "SELECT AVG(\"reviews\".\"stars\") AS avg_id FROM \"reviews\" WHERE \"users\".\"reviewed_user_id\" = 42" 
+0

Super! Gracias por compartir el método que utilizó para resolver esto ... ¡será útil en el futuro! – Zabba

+0

Es por eso que amo a Ruby. Siempre puedes pedir el REPL y la fuente. ¡Es la fuente en más de un sentido de la palabra! –

+0

Esto me ayuda mucho en muchas lógicas diferentes. gran hombre de trabajo. –

Cuestiones relacionadas