Anular un Rails default_scope

156

Si tengo un modelo ActiveRecord :: Base con un alcance predeterminado:

class Foo < ActiveRecord::Base

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

end

¿Hay alguna forma de hacerlo Foo.find sin usar las default_scopecondiciones? En otras palabras, ¿puede anular un alcance predeterminado?

Pensé que usar 'default' en el nombre sugeriría que era invalidable, de lo contrario se llamaría algo así global_scope, ¿verdad?

Gareth
fuente

Respuestas:

151

Respuesta corta: no lo use a default_scopemenos que realmente tenga que hacerlo. Probablemente estará mejor con ámbitos con nombre. Dicho esto, puede usar with_exclusive_scopepara anular el alcance predeterminado si es necesario.

Echa un vistazo a esta pregunta para más detalles.

Pär Wieslander
fuente
Gracias por el enlace a la pregunta anterior
Gareth, el
10
> No use default_scope a menos que realmente tenga que hacerlo. Un excelente consejo! ¡Gracias!
installero
2
Tan verdadero. El uso default_scopepuede parecer una buena idea, pero probablemente causará múltiples dolores de cabeza durante la vida útil de su aplicación.
thomax
1
Estás exagerando un poco señor. default_scopees una herramienta excelente y hay situaciones en las que podría default_scopehacerlo de otra manera, pero es lo correcto. Por ejemplo, cuando tiene un Productmodelo que tiene una inactivebandera, default_scope { where inactive: false }lo mejor es configurar un a, ya que en 99% o casos no querrá mostrar un producto inactivo. Luego solo llama unscopedal 1% de casos restantes, que probablemente sea un panel de administración.
pedrozath
215

En Rails 3:

foos = Foo.unscoped.where(:baz => baz)
Vincent
fuente
58
Esto tiene un efecto secundario, si Post has_many Comment, Post.first.comments.unscoped devuelve TODOS los comentarios.
Enrico Carlesso
3
Esto realmente me jodió por un tiempo. Especialmente si termina poniendo esto en un método de clase como: def self.random; unscoped.order('rand()'); endunscoped elimina ALL sql antes, no solo lo que se enumera en default_scope. Aunque técnicamente es una respuesta correcta, tenga cuidado de usarunstopped
Schneems
77
¡ADVERTENCIA! Unscoped NO elimina solo el default_scope, ya se dijo en otro comentario, pero realmente puede estropear las cosas.
dsimard
15
Una buena regla general es solo unscopedcuando puede seguir directamente un modelo, por ejemplo, Foo.unscoped.blah()está bien pero nuncaFoo.blah().unscoped .
Grant Birchmeier el
stackoverflow.com/questions/1834159/… funciona alrededor del efecto secundario mencionado por Enrico
wbharding
107

Si todo lo que necesita es cambiar el orden definido en default_scope, puede usar el reordermétodo .

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

Foo.reorder('created_at asc')

ejecuta el siguiente SQL:

SELECT * FROM "foos" ORDER BY created_at asc
GuiGS
fuente
3
Consejo: defina un ámbito como scope :without_default_order, -> { reorder("") }y puede hacer cosas como Foo.without_default_order.order("created_at ASC")En algunas situaciones, se lee mejor (tal vez no sea esta situación exacta, pero tuve una).
Henrik N
Reordenar lo hizo por mí. ¡Muchas gracias!
Andre Zimpel
49

Como 4.1puede usar ActiveRecord::QueryMethods#unscopepara combatir el alcance predeterminado:

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

Es actualmente posible unscopecosas como: :where, :select, :group, :order, :lock, :limit, :offset, :joins, :includes, :from, :readonly, :having.

Pero aún así , evite usar default_scopesi puede . Es por tu propio bien.

jibiel
fuente
Esta respuesta debería ser mayor
Stephen Corwin, el
5

Rails 3 default_scope no parece anularse como lo hizo en Rails 2.

p.ej

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"

En mi aplicación, usando PostgreSQL, el orden en el alcance predeterminado WINS. Estoy eliminando todos mis default_scopes y codificándolo explícitamente en todas partes.

Pitfall Rails3!

vanboom
fuente
1
Tienes que usarBar.foos.reorder(:created_at => :asc)
Ivan Stana
5

Con Rails 3+ puede usar una combinación de sin ámbito y fusión:

# model User has a default scope
query = User.where(email: "[email protected]")

# get rid of default scope and then merge the conditions
query = query.unscoped.merge(query)
santervo
fuente
Esto también funcionó para mí, llamar primero sin alcance (Rails 4.2):User.unscoped.where(email: "[email protected]")
Nick
4

En Rails 5.1+ (y tal vez antes, pero he probado que funciona en 5.1), es posible quitar el alcance de una columna específica, lo que, en mi opinión, es la solución ideal para eliminar una default_scopede una manera que se puede usar dentro de un alcance con nombre. En el caso de los PO default_scope,

Foo.unscope(where: :bar)

O

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

Ambos darán como resultado una consulta sql que no aplica el alcance original, pero sí aplica cualquier otra condición que se combine en el arel.

wbharding
fuente
2

Bueno, siempre puedes usar el favorito de antaño find_by_sqlcon la consulta completa. Por ejemplo: Model.find_by_sql ("SELECT * FROM modelos WHERE id = 123")

Ady Rosen
fuente