Sostituzione di un Rails default_scope


156

Se ho un modello ActiveRecord :: Base con un ambito predefinito:

class Foo < ActiveRecord::Base

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

end

C'è un modo per fare un Foo.find senza usare le default_scopecondizioni? In altre parole, puoi sovrascrivere un ambito predefinito?

Avrei pensato che l'uso di "default" nel nome avrebbe suggerito che fosse sovrascrivibile, altrimenti sarebbe stato chiamato qualcosa del genere global_scope, giusto?


Risposte:


151

Risposta breve: non utilizzare a default_scopemeno che non sia necessario. Probabilmente starai meglio con gli ambiti denominati. Detto questo, è possibile utilizzare with_exclusive_scopeper sovrascrivere l'ambito predefinito, se necessario.

Dai un'occhiata a questa domanda per maggiori dettagli.


Grazie per il link alla domanda precedente
Gareth,

10
> Non utilizzare default_scope a meno che non sia necessario. Un consiglio eccellente! Grazie!
installazione

2
Così vero. L'uso default_scopepotrebbe sembrare una buona idea, ma probabilmente causerà più mal di testa durante la vita della tua app.
Thomax


1
Stai esagerando un po 'signore. default_scopeè uno strumento eccellente e ci sono situazioni in cui potresti fare un altro modo, ma è default_scopela cosa giusta da fare. Ad esempio, quando hai un Productmodello che ha un inactiveflag, impostare a default_scope { where inactive: false }è la cosa migliore da fare, dal 99% o dai casi non vorrai visualizzare un prodotto inattivo. Quindi devi solo chiamare unscopedi restanti casi dell'1%, che è probabilmente un pannello di amministrazione.
Pedrozath,

215

In Rails 3:

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

58
Ciò ha un effetto collaterale se Post has_many Comment, Post.first.comments.unscoped restituisce TUTTI i commenti.
Enrico Carlesso,

3
Questo mi ha davvero rovinato per un po '. Soprattutto se def self.random; unscoped.order('rand()'); endfinisci per inserirlo in un metodo di classe come: unscoped rimuove TUTTO sql prima di esso, non solo ciò che è elencato in default_scope. Mentre tecnicamente è una risposta corretta, fai attenzione usandounstopped
Schneems il

7
AVVERTIMENTO! Unscoped NON rimuove solo default_scope, è già stato detto in un altro commento, ma può davvero rovinare le cose.
dsimard,

15
Una buona regola empirica è solo unscopedquando può seguire direttamente un modello, ad esempio Foo.unscoped.blah()è ok ma mai Foo.blah().unscoped.
Grant Birchmeier,

stackoverflow.com/questions/1834159/… lavora attorno all'effetto collaterale menzionato da Enrico
wbharding,

107

Se tutto ciò di cui hai bisogno è cambiare l'ordine definito in default_scope, puoi usare il reordermetodo .

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

Foo.reorder('created_at asc')

esegue il seguente SQL:

SELECT * FROM "foos" ORDER BY created_at asc

3
Suggerimento: definisci un ambito simile scope :without_default_order, -> { reorder("") }e puoi fare cose del genere Foo.without_default_order.order("created_at ASC")In alcune situazioni è meglio leggere (forse non questa situazione esatta, ma ne avevo una).
Henrik N,

Reorder l'ha fatto per me. Molte grazie!
Andre Zimpel,

49

Dal momento che 4.1è possibile utilizzare ActiveRecord::QueryMethods#unscopeper combattere l'ambito predefinito:

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

E ' attualmente possibile unscopecose del genere: :where, :select, :group, :order, :lock, :limit, :offset, :joins, :includes, :from, :readonly, :having.

Ma comunque, per favore, evita di usarlo default_scopese puoi . È per il tuo bene.


Questa risposta dovrebbe essere più alta
Stephen Corwin,


5

Rails 3 default_scope non sembra essere sostituito come in Rails 2.

per esempio

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"

Nella mia app, usando PostgreSQL, l'ordinamento nell'ambito predefinito WINS. Sto rimuovendo tutti i miei default_scopes e li codifico esplicitamente ovunque.

Pitfall Rails3!


1
Devi usareBar.foos.reorder(:created_at => :asc)
Ivan Stana il

5

Con Rails 3+ puoi utilizzare una combinazione di non scavato e unire:

# 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)

Questo ha funzionato anche per me, per prima cosa chiamare senza ambito (Rails 4.2):User.unscoped.where(email: "foo@example.com")
Nick,

4

Su Rails 5.1+ (e forse prima, ma ho provato che funziona su 5.1) è possibile annullare la definizione di una colonna specifica, che rappresenta la soluzione ideale per rimuovere un default_scopein un modo che può essere utilizzato all'interno di un ambito denominato. Nel caso dei PO default_scope,

Foo.unscope(where: :bar)

O

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

Entrambi si tradurranno in una query sql che non applica l'ambito originale, ma si applica a qualsiasi altra condizione venga unita all'arel.


2

Bene, puoi sempre usare il preferito dei vecchi tempi find_by_sqlcon la query completa. Ad esempio: Model.find_by_sql ("SELEZIONA * DA modelli DOVE ID = 123")

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.