2009-06-01 18 views
21

Estoy acostumbrado a Django, donde puede ejecutar varios métodos de filtro en querysets, es decir, Item.all.filter(foo="bar").filter(something="else").Filtrado de consultas de ActiveRecord en rieles

Sin embargo, esto no es tan fácil de hacer en Rails. Item.find(:all, :conditions => ["foo = :foo", { :foo = bar }]) devuelve una matriz que significa que esto no funcionará:

Item.find(:all, :conditions => ["foo = :foo", { :foo = 'bar' }]).find(:all, :conditions => ["something = :something", { :something = 'else' }])

Así que pensé que la mejor manera de filtros "apilar" es modificar las condiciones de la matriz y luego ejecutar la consulta.

Así me ocurrió con esta función:

def combine(array1,array2) 
    conditions = [] 
    conditions[0] = (array1[0]+" AND "+array2[0]).to_s 
    conditions[1] = {} 
    conditions[1].merge!(array1[1]) 
    conditions[1].merge!(array2[1]) 
    return conditions 
end 

Uso:

array1 = [ "foo =: ​​foo", {: 'bar' foo =}] matriz2 = [ "algo =: algo", {: algo = 'else'}] = condiciones combinan (matriz1, array2) artículos = Item.find (: todos,: condiciones => condiciones)

Esto ha funcionado bastante bien. Sin embargo, quiero ser capaz de combinar un número arbitrario de matrices, o básicamente abreviada de la escritura:

conditions = combine(combine(array1,array2),array3) 

¿Alguien puede ayudar con esto? Gracias por adelantado.

Respuesta

35

alcances lo que quieres se denominan:

class Item < ActiveRecord::Base 
    named_scope :by_author, lambda {|author| {:conditions => {:author_id => author.id}}} 
    named_scope :since, lambda {|timestamp| {:conditions => {:created_at => (timestamp .. Time.now.utc)}}} 
    named_scope :archived, :conditions => "archived_at IS NOT NULL" 
    named_scope :active, :conditions => {:archived_at => nil} 
end 

En los controladores, utilice la siguiente manera:

class ItemsController < ApplicationController 
    def index 
    @items = Item.by_author(current_user).since(2.weeks.ago) 
    @items = params[:archived] == "1" ? @items.archived : @items.active 
    end 
end 

El objeto devuelto es un proxy y la consulta SQL, no arrancara hasta que realmente comience a hacer algo real con la colección, como iterar (para mostrar) o cuando llame a métodos Enumerable en el proxy.

+0

Sí, eso es exactamente lo que quiero. Vi el screencast y me pareció totalmente lógico. Pero tan pronto como encendí el código, recibí un error que decía que el alcance nombrado era un método indefinido. Entonces me di cuenta de que estaba trabajando con Rails 1.8, así que actualicé los rieles a 2.3.algo y obtuve el mismo error ... ugh, sabía que los ámbitos con nombre eran demasiado buenos para ser verdad:/ – user94154

+1

Si congelaste Rails, entonces tu código todavía está usando una versión previa de Rails. Rails 1.8 nunca existió, por lo que debe referirse a Ruby 1.8. rails -v es un comando que le dirá qué versión de Rails existe en la línea de comando. script/about le dirá más sobre el entorno de su aplicación. –

+0

bien el script de ruby ​​/ about no fue reconocido. ¿Qué significa congelado en términos de Rails? – user94154

1

Puede echar un vistazo al Searchlogic.
Hace que sea más fácil usar las condiciones en los conjuntos ActiveRecord, e incluso en Arrays.

Espero que ayude.

1

Puede (o al menos solía ser capaz de) filtrar como tal en rieles:

find(:all, :conditions => { :foo => 'foo', :bar => 'bar' }) 

donde: foo y: bar son campo nombres en el registro activo. Parece que todo lo que necesitas hacer es pasar un hash de: field_name => value pairs.

+0

Esto funcionó para mí (estoy en 1.8.7) – John

6

No lo haría como usted propuso.

Desde encontrar devolver una matriz, puede utilizar métodos de arreglos filtrarlo, por ejemplo:

Item.find(:all).select {|i| i.foo == bar }.select {|i| i.whatever > 23 }... 

También puede achive lo que quiere con ámbitos mencionados.

+1

Los ámbitos con nombre son una forma mucho mejor de hacerlo y le proporcionan métodos de uso común fácilmente reutilizables que puede usar. – nitecoder

+1

@railsninja: Estoy de acuerdo, pero debe definir el alcance con nombre antes de usarlo. Si desea hacer algo solo una vez, es más fácil usar seleccionar. – klew

+2

No es cierto. Puede hacer alcances anónimos sobre la marcha: http://railscasts.com/episodes/112-anonymous-scopes –

Cuestiones relacionadas