Как вернуть пустое отношение ActiveRecord? - PullRequest
226 голосов
/ 02 февраля 2011

Если у меня есть область видимости с лямбдой, и она принимает аргумент, в зависимости от значения аргумента, я могу знать, что совпадений не будет, но я все же хочу вернуть отношение, а не пустой массив:

scope :for_users, lambda { |users| users.any? ? where("user_id IN (?)", users.map(&:id).join(',')) : [] }

Что мне действительно нужно, так это метод «none», противоположный «all», который возвращает отношение, которое все еще может быть связано, но приводит к короткому замыканию запроса.

Ответы [ 9 ]

437 голосов
/ 04 апреля 2012

В Rails 4 теперь есть «правильный» механизм:

>> Model.none 
=> #<ActiveRecord::Relation []>
75 голосов
/ 16 марта 2011

Более переносимое решение, которое не требует столбца "id" и не предполагает, что не будет строки с идентификатором 0:

scope :none, where("1 = 0")

Я все еще ищу более "правильный" способ.

43 голосов
/ 23 октября 2012

Прибытие в Rails 4

В Rails 4 цепочка ActiveRecord::NullRelation будет возвращена из вызовов типа Post.none.

Ни ее, ни цепочечные методы не будут генерировать запросы кбаза данных.

Согласно комментариям:

Возвращенный ActiveRecord :: NullRelation наследуется от Relation и реализует шаблон Null Object.Это объект с определенным нулевым поведением, который всегда возвращает пустой массив записей без запроса к базе данных.

См. Исходный код .

42 голосов
/ 21 февраля 2011

Вы можете добавить область под названием «none»:

scope :none, where(:id => nil).where("id IS NOT ?", nil)

Это даст вам пустой ActiveRecord :: Relation

Вы также можете добавить его в ActiveRecord :: Base в инициализаторе (если хотите):

class ActiveRecord::Base
 def self.none
   where(arel_table[:id].eq(nil).and(arel_table[:id].not_eq(nil)))
 end
end

Множество способов получить что-то подобное, но, конечно, не самое лучшее, что можно сохранить в кодовой базе. Я использовал область действия: ни один при рефакторинге и обнаружил, что мне нужно в течение короткого времени гарантировать пустой ActiveRecord :: Relation.

24 голосов
/ 12 апреля 2012
scope :none, limit(0)

Это опасное решение, потому что ваша область может быть прикована цепью.

User.none.first

вернет первого пользователя.Безопаснее использовать

scope :none, where('1 = 0')
14 голосов
/ 22 июня 2011

Мне кажется, я предпочитаю, чтобы это выглядело по сравнению с другими вариантами:

scope :none, limit(0)

Что-то вроде этого:

scope :users, lambda { |ids| ids.present? ? where("user_id IN (?)", ids) : limit(0) }
3 голосов
/ 02 февраля 2011

Использование scoped:

scope :for_users, lambda { |users| users.any? ? where("user_id IN (?)", users.map(&:id).join(',')) : scoped }

Но вы также можете упростить свой код с помощью:

scope :for_users, lambda { |users| where(:user_id => users.map(&:id)) if users.any? }

Если вы хотите получить пустой результат, используйте это (удалите условие if):

scope :for_users, lambda { |users| where(:user_id => users.map(&:id)) }
1 голос
/ 13 мая 2015

Можно и так вот:

scope :for_users, lambda { |users| users.any? ? where("user_id IN (?)", users.map(&:id).join(',')) : User.none }

http://apidock.com/rails/v4.0.2/ActiveRecord/QueryMethods/none

Поправь меня, если я ошибаюсь.

1 голос
/ 17 октября 2014

Есть также варианты, но все они делают запрос к db

where('false')
where('null')
...