views:

1570

answers:

5

I'm used to Django where you can run multiple filter methods on querysets, ie Item.all.filter(foo="bar").filter(something="else").

The however this is not so easy to do in Rails. Item.find(:all, :conditions => ["foo = :foo", { :foo = bar }]) returns an array meaning this will not work:

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

So I figured the best way to "stack" filters is to modify the conditions array and then run the query.

So I came up with this function:

 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

Usage:

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

This has worked pretty well. However I want to be able to combine an arbitrary number of arrays, or basically shorthand for writing:

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

Can anyone help with this? Thanks in advance.

A: 

You can take a look at Searchlogic.
It makes it easier to use conditions on ActiveRecord sets, and even on Arrays.

Hope it helps.

andi
+3  A: 

I think what you want is this: Named Scope

John Munsch
A: 

You can (or at least used to be able to) filter like so in Rails:

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

where :foo and :bar are field names in the active record. Seems like all you need to do is pass in a hash of :field_name => value pairs.

workmad3
that's :conditions => {:foo ...}
Oliver N.
You need to add => after :conditions
klew
+2  A: 

I wouldn't do it like you proposed.

Since find return an array, you can use array methods to filter it, on example:

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

You can also achive what you want with named scopes.

klew
+1 I like your answer best
Oliver N.
Named scopes are much better way of doing this and it gives you easily re usable chainable methods that you can use.
railsninja
@railsninja: I agree, but you have to define named scope before you use it. If you want to do something only once, it is easier to use select.
klew
@klew: Not true. You can do anonymous scopes on the fly: http://railscasts.com/episodes/112-anonymous-scopes
Adam Lassek
+5  A: 

What you want are named scopes:

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

In your controllers, use like this:

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

The returned object is a proxy and the SQL query will not be run until you actually start doing something real with the collection, such as iterating (for display) or when you call Enumerable methods on the proxy.

François Beausoleil
Yep that is exactly what I want. I watched the screencast and it made total sense to me. But as soon as I fired up the code I got an error saying that named scope was an undefined method. Then I noticed that I was working with Rails 1.8, so I gem updated rails to 2.3.something and still got the same error....ugh, I knew named scopes were too good to be true :/
If you froze Rails, then your code is still using a previous version of Rails. Rails 1.8 never existed, so you must mean Ruby 1.8. rails -v is a command that will tell you what version of Rails exists at the command line. script/about will tell you more about your application's environment.
François Beausoleil
well ruby script/about wasn't recognized. What does frozen mean in terms of Rails?
You have frozen Rails if the framework is copied into Vendor/rails directory in your project. But I think it's not a problem in your case, you probably have old version number in your environment.rb (look for RAILS_GEM_VERSION).
klew