Переопределення Rails default_scope


155

Якщо у мене є модель ActiveRecord :: Base із за замовчуванням:

class Foo < ActiveRecord::Base

  default_scope :conditions => ["bar = ?",bar]

end

Чи є спосіб обійтися Foo.find без використання default_scopeумов? Іншими словами, чи можете ви змінити область за замовчуванням?

Я б подумав , що з допомогою « по умовчанням» в ім'я буде припустити , що це було перевизначення, в іншому випадку це буде називатися що - щось на зразок global_scope, НЕ так?


Відповіді:


151

Коротка відповідь: Не використовуйте, default_scopeякщо вам справді не доведеться. Ймовірно, вам буде краще з названими областями. Зважаючи на це, ви можете використовувати with_exclusive_scopeдля зміни функції за замовчуванням, якщо потрібно.

Погляньте на це питання для отримання більш детальної інформації.


Дякуємо за посилання на попереднє запитання
Гарет

10
> Не використовуйте default_scope, якщо вам цього не потрібно. Відмінна порада! Дякую!
installero

2
Такий справжній. Використання default_scopeможе здатися гарною ідеєю, але, ймовірно, спричинить багаторазові головні болі протягом життя вашої програми.
томакс


1
Ви трохи перебільшуєте, сер. default_scopeце чудовий інструмент, і є ситуації, коли ви можете іншим способом, але default_scopeце тільки правильне. Наприклад, коли у вас є Productмодель, на якій є inactiveпрапор, default_scope { where inactive: false }найкраще зробити встановлення a , оскільки 99% або випадки ви не захочете відображати неактивний продукт. Тоді ви просто зателефонуєте unscopedна 1% випадків, що, ймовірно, панель адміністратора.
педрозат

215

У рейках 3:

foos = Foo.unscoped.where(:baz => baz)

58
Це має побічний ефект, якщо допис has_many коментар, Post.first.comments.unscoped повертає ВСІ коментарі.
Енріко Карлессо

3
Це мене справді накрутило на деякий час. Особливо, якщо ви в кінцевому підсумку ставите це методом класу на кшталт: def self.random; unscoped.order('rand()'); enduncoped видаляє ВСЕ sql перед ним, а не лише те, що вказано під default_scope. Хоча технічно правильна відповідь, будьте обережні, використовуючиunstopped
Schneems

7
УВАГА! Нескопаний НЕ знімає лише default_scope, це вже було сказано в іншому коментарі, але він справді може зіпсувати речі.
dsimard

15
Добре правило - це лише unscopedтоді, коли він може безпосередньо слідувати за моделлю, наприклад, Foo.unscoped.blah()це нормально, але ніколи Foo.blah().unscoped.
Грант Бірхмайер

stackoverflow.com/questions/1834159/… працює навколо побічного ефекту, згаданого Енріко
wbharding

107

Якщо все, що вам потрібно, це змінити порядок, визначений у default_scope, ви можете скористатися reorderметодом .

class Foo < ActiveRecord::Base
  default_scope order('created_at desc')
end

Foo.reorder('created_at asc')

запускає такий SQL:

SELECT * FROM "foos" ORDER BY created_at asc

3
Порада: визначте сферу на зразок, scope :without_default_order, -> { reorder("") }і ви можете робити такі речі, як Foo.without_default_order.order("created_at ASC")у деяких ситуаціях вона читається краще (можливо, це не зовсім точна ситуація, але у мене була така).
Генрік Н

Перепорядок зробив це для мене. Дуже дякую!
Андре

49

Оскільки 4.1ви можете використовувати ActiveRecord::QueryMethods#unscopeдля боротьби з типовою сферою застосування:

class User < ActiveRecord::Base
  default_scope { where tester: false }
  scope :testers, -> { unscope(:where).where tester: true }
  scope :with_testers, -> { unscope(:where).where tester: [true, false] }
  # ...
end

Це в даний час можливо такі unscopeречі , як: :where, :select, :group, :order, :lock, :limit, :offset, :joins, :includes, :from, :readonly, :having.

Але все ж будь ласка, уникайте використання, default_scopeякщо можете . Це для вашого блага.


Ця відповідь повинна бути вищою
Стівен Корвін


5

Здійснюється, що для Rails 3 default_scope не буде перекрито так, як це було в Rails 2.

напр

class Foo < ActiveRecord::Base
  belongs_to :bar
  default_scope :order=>"created_at desc"
end

class Bar < ActiveRecord::Base
  has_many :foos
end

> Bar.foos
  SELECT * from Foo where bar_id = 2 order by "created_at desc";
> Bar.unscoped.foos
  SELECT * from Foo;  (WRONG!  removes the "has" relationship)
> Bar.foos( :order=>"created_at asc" )  # trying to override ordering
  SELECT * from Foo where bar_id = 2 order by "created_at desc, created_at asc"

У моєму додатку, використовуючи PostgreSQL, упорядкування в області WINS за замовчуванням. Я видаляю всі мої default_scopes і кодую їх явно скрізь.

Підводні рейки3!


1
Ви повинні скористатисяBar.foos.reorder(:created_at => :asc)
Іван Стана

5

За допомогою Rails 3+ ви можете використовувати комбінацію незакритих та злиття:

# model User has a default scope
query = User.where(email: "foo@example.com")

# get rid of default scope and then merge the conditions
query = query.unscoped.merge(query)

Це також працювало для мене, щоб спочатку зателефонувати неспецифічно (Rails 4.2):User.unscoped.where(email: "foo@example.com")
Нік

3

На Rails 5.1+ (а може і раніше, але я тестував, що це працює на 5.1), можна скасувати конкретний стовпець, який imho є ідеальним рішенням для видалення default_scopeмод, який можна використовувати всередині названої області. У випадку з ОПdefault_scope ,

Foo.unscope(where: :bar)

Або

scope :not_default, -> { unscope(where: :bar) }
Foo.not_default

Це призведе до запиту sql, який не застосовує початковий діапазон, але застосовує будь-які інші умови, об'єднані в arel.


2

Що ж, ви завжди можете використовувати старий улюблений час find_by_sqlіз повним запитом. Наприклад: Model.find_by_sql ("SELECT * FROM models WHERE id = 123")

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.