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

156

Если у меня есть ActiveRecord :: Base модель с областью действия по умолчанию:

class Foo < ActiveRecord::Base

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

end

Есть ли способ сделать Foo.find без использования default_scopeусловий? Другими словами, можете ли вы переопределить область по умолчанию?

Я бы подумал , что с помощью « по умолчанию» во имя будет предположить , что это было переопределение, в противном случае это будет называться что - то вроде global_scope, не так ли?

Gareth
источник

Ответы:

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 }лучше всего сделать настройку , так как в 99% случаев вы не захотите отображать неактивный продукт. Затем вы просто вызываете unscopedоставшиеся 1% случаев, которые, вероятно, являются панелью администратора.
Педрозат
215

В Rails 3:

foos = Foo.unscoped.where(:baz => baz)
Винсент
источник
58
Это имеет побочный эффект, если Post has_many Comment, Post.first.comments.unscoped возвращает ВСЕ комментарии.
Энрико Карлессо
3
Это действительно напортачило меня на некоторое время. Особенно, если вы в конечном итоге поместите это в метод класса, например: def self.random; unscoped.order('rand()'); endunscoped удаляет ВСЕ sql перед ним, а не только то, что указано в default_scope. Хотя технически правильный ответ, будьте осторожны, используяunstopped
Schneems
7
ПРЕДУПРЕЖДЕНИЕ! Unscoped НЕ удаляет только default_scope, это уже было сказано в другом комментарии, но это может действительно испортить вещи.
Дсимард
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
GuiGS
источник
3
Совет: определите область видимости как, scope :without_default_order, -> { reorder("") }и вы можете сделать что-то вроде: Foo.without_default_order.order("created_at ASC")В некоторых ситуациях она читается лучше (возможно, не в этой точной ситуации, но у меня была такая).
Хенрик Н
Reorder сделал это для меня. Большое спасибо!
Андре Цимпель
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если можете . Это для вашего же блага.

jibiel
источник
Этот ответ должен быть выше
Стивен Корвин
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 и кодирую это явно везде.

Касаемо Rails3!

vanboom
источник
1
Вы должны использоватьBar.foos.reorder(:created_at => :asc)
Иван Стана
5

С Rails 3+ вы можете использовать комбинацию unscoped и merge:

# 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)
santervo
источник
Это также сработало для меня, чтобы сначала назвать незаданным (Rails 4.2):User.unscoped.where(email: "foo@example.com")
Ник
4

На 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.

wbharding
источник
2

Ну, вы всегда можете использовать старые фавориты find_by_sqlс полным запросом. Например: Model.find_by_sql («ВЫБРАТЬ * ИЗ МОДЕЛЕЙ, ГДЕ id = 123»)

Ади Розен
источник