web-dev-qa-db-ja.com

railsでのActiveRecordクエリのフィルタリング

私はDjango=に慣れています。ここでは、クエリセットで複数のフィルターメソッドを実行できます(つまり、Item.all.filter(foo="bar").filter(something="else"))。

ただし、これはRailsで行うのはそれほど簡単ではありません。 Item.find(:all, :conditions => ["foo = :foo", { :foo = bar }])は、これが機能しないことを意味する配列を返します。

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

したがって、フィルターを「スタック」する最良の方法は、条件配列を変更してからクエリを実行することであると考えました。

だから私はこの機能を思いつきました:

 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

使用法:

array1 = ["foo =:foo"、{:foo = 'bar'}] array2 = ["something =:something"、{:something = 'else'}]条件= bind(array1、array2)items =アイテム。 find(:all、:conditions =>条件)

これはかなりうまくいきました。しかし、私は任意の数の配列を組み合わせることができるようにしたい、または基本的に書くための省略形です:

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

誰かがこれを手伝ってくれる?前もって感謝します。

24
user94154

あなたが欲しいものは名前付きスコープです:

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

コントローラーでは、次のように使用します。

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

返されたオブジェクトはプロキシであり、SQLクエリは、コレクションの実際の処理(たとえば、表示)を行うか、プロキシでEnumerableメソッドを呼び出すまで実行されません。

39

あなたが提案したように私はそれをしません。

Findは配列を返すため、配列メソッドを使用してそれをフィルタリングできます。次に例を示します。

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

名前付きスコープで必要なものを実現することもできます。

7
klew

Searchlogic をご覧ください。
これにより、ActiveRecordセット、およびArraysでも条件を使用しやすくなります。

それが役に立てば幸い。

1
andi

Railsでは、次のようにフィルタリングできます(または、少なくとも使用できるようになります)。

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

ここで、:fooと:barはアクティブレコードのフィールド名です。 :field_name =>値のペアのハッシュを渡すだけでよいようです。

1
workmad3