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.
Active Record is the M in MVC - the model - which is the layer of the system responsible for representing business data and logic. Active Record facilitates the creation and use of business objects whose data requires persistent storage to a database.
ActiveRecord is an ORM. It's a layer of Ruby code that runs between your database and your logic code. When you need to make changes to the database, you'll write Ruby code, and then run "migrations" which makes the actual changes to the database.
Eager loading solves this problem by creating a left outer join on the table, returning all of the data in a single query. Adding an eager load is as simple as adding an :includes statement to the controller.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With