2015-04-15 2 views
3

У меня возникли трудности с фильтрацией необязательных параметров запроса.Запросы на привязку Rails

def index 
    @books = Book.where(nil) 
    @books = @books.status(params[:status]) if params[:status].present? 
    @books = @books.location(params[:location]) if params[:location].present? 
    @books = @books.starts_with(params[:starts_with]) if params[:starts_with].present? 
end 

Я нашел пример, очень похожий на этот онлайн. Моя проблема связана с дополнительными параметрами. Например, что я хочу фильтровать только для: status и: location или filter для: location и: starts_with? Не знаю, что делать ...

+0

Check [этот ответ] (http://stackoverflow.com/a/4480139/2835243). Это должно делать то, что вы хотите сделать. – TheWanderingMind

ответ

0

Вы можете сделать следующее:

def index 
    @books = Book.scoped 
    @books = @books.where(status: params[:status]) if params[:status].present? 
    @books = @books.where(location: params[:location]) if params[:location].present? 
    @books = @books.where(starts_with: params[:starts_with]) if params[:starts_with].present? 
end 

Но я не уверен, что атрибут starts_with ...

+0

Есть ли причина, почему? – codecalypso

+0

Кроме того, это не Book.scoped то же, что и Book.where (nil)? – codecalypso

+0

@codecalypso 'Book.where (nil)' такой же, как 'Book.scoped', но я нахожу' Book.scoped' менее запутанным, чем 'where (nil)'. Я не понимаю ваш первый комментарий: я не понимаю, что вы хотите, чтобы я оправдывал. – MrYoshiji

2

На данный момент вы, возможно, потребуется перейдите к чистому AREL и передайте их в качестве параметров для метода поиска. Что дает некоторые интересные дополнительные predicates и более расширяемой

вот и грязный, с верхней части моей головы, например, как вы могли сделать это с AREL. Его непроверенный код, хотя ... он не должен быть открытым до SQL injection, но я не помню, если AREL санирует совпадения запросов. Кроме того, подход AREL по совпадению предпочтительнее, чем необработанный SQL, поскольку он должен быть агностиком DB.

class Book 
    ... 
    # search class method via AREL 
    def self.search(params = {}) 
    if params.respond_to?(:has_key?) 
     # setup arel object for proper table 
     books = Arel::Table.new(:books) 

     if params.has_key?(:location) 
     location_match = books[:location].eq(params[:location]) 
     end 

     if params.has_key?(:status) 
     status_match = books[:status].eq(params[:status]) 
     end 

     # although if all you're doing is searching for a title 
     # maybe you can deprecate starts_with column and search 
     # the proper column say... title 
     if params.has_key?(:starts_with) 
     title_match = books[:starts_with].matches("%#{params[:starts_with}%") 
     end 

     # return AREL query. Note this *should* be safe from [SQL injection][4] 
     # via AREL sanitization but verify then trust. 

     # Choose one of the below 
     # match on Any 
     where(location_match.or(status_match).or(title_match)) 

     # match on ALL 
     where(location_match.and(status_match).and(title_match)) 
    end 
    end 
    ... 
end 

Или более сложная настройка, но более простой подход заключается в использовании search kick камня и настраивают elasticsearch экземпляра.

Также быстрое примечание стороны. Проверка ключей Хэша с присутствием Hash #? это запах кода. Что произойдет, если проверяемый объект не реагирует на индексном метод ... Он будет дуть с

(dev)> a = nil 
=> nil 
(dev)> a[:dave].present? 
NoMethodError: undefined method `[]' for nil:NilClass 

IMO Вы должны более правильно проверить, что сам хэш существует, а затем использовать метод рубина has_key? (: some_key) ...

(dev)> a = nil 
=> nil 
(dev)> a.present? && a.has_key?(:dave) 
=> false 
(dev)> 

Но тогда это только я, ваш пробег может отличаться. В принципе никогда не доверяйте типам ruby ​​тем, что вы ожидаете от них.

Примечание. В этом методе мы устанавливаем хэш по умолчанию, если ни один не передан, поэтому мы можем перейти к has_key? проверьте, однако он все равно может исчезнуть, если мы передадим ему что-то еще, следовательно, response_to? проверьте параметры.

Теперь, когда вы вызываете его в контроллер (или модели, или там, где) вы можете назвать в качестве такого

@books = Book.search(params) 
Смежные вопросы