Фильтрация запросов ActiveRecord в рельсах - PullRequest
24 голосов
/ 02 июня 2009

Я привык к 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'}] массив2 = ["что-то =: что-то", {: что-то = 'еще'}] условия = объединить (массив1, массив2) items = Item.find (: все,: условия => условия)

Это сработало довольно хорошо. Однако я хочу иметь возможность комбинировать произвольное количество массивов или в основном сокращение для записи:

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

Может кто-нибудь помочь с этим? Заранее спасибо.

Ответы [ 4 ]

39 голосов
/ 02 июня 2009

То, что вы хотите, называется областями действия:

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 на прокси.

7 голосов
/ 02 июня 2009

Я бы не сделал это так, как вы предложили.

Поскольку find возвращает массив, вы можете использовать методы массива для его фильтрации, например:

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

Вы также можете достичь желаемого с помощью именованных областей.

1 голос
/ 02 июня 2009

Вы можете (или, по крайней мере, раньше могли) фильтровать, как в Rails:

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

где: foo и: bar - имена полей в активной записи. Похоже, все, что вам нужно сделать, это передать хэш из: field_name => пары значений.

1 голос
/ 02 июня 2009

Вы можете взглянуть на Searchlogic .
Это облегчает использование условий на ActiveRecord комплектов и даже на Arrays.

Надеюсь, это поможет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...