Przesłanianie default_scope Rails

155

Jeśli mam model ActiveRecord :: Base z domyślnym zakresem:

class Foo < ActiveRecord::Base

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

end

Czy można to zrobić Foo.find bez korzystania z default_scopewarunków? Innymi słowy, czy możesz przesłonić domyślny zakres?

Myślałem, że za pomocą „default” w nazwie sugeruje, że był przeciążać, inaczej go nazwać coś takiego global_scope, prawda?

Gareth
źródło

Odpowiedzi:

151

Krótka odpowiedź: nie używaj, default_scopechyba że naprawdę musisz. Prawdopodobnie lepiej będzie, jeśli skorzystasz z nazwanych zakresów. Mając to with_exclusive_scopena uwadze , możesz użyć, aby zastąpić domyślny zakres, jeśli zajdzie taka potrzeba.

Spójrz na to pytanie, aby uzyskać więcej informacji.

Pär Wieslander
źródło
Dzięki za link do poprzedniego pytania
Gareth,
10
> Nie używaj default_scope, chyba że naprawdę musisz. Doskonała rada! Dziękuję Ci!
installero
2
Tak prawdziwe. Używanie default_scopemoże wydawać się dobrym pomysłem, ale prawdopodobnie spowoduje wiele bólów głowy w okresie użytkowania aplikacji.
thomax
1
Trochę przesadzasz, sir. default_scopeto doskonałe narzędzie i są sytuacje, w których możesz inaczej, ale default_scopejest to po prostu słuszne. Na przykład, jeśli masz Productmodel, który ma inactiveflagę, ustawienie a default_scope { where inactive: false }jest najlepszą rzeczą do zrobienia, ponieważ w 99% przypadków nie będziesz chciał wyświetlać nieaktywnego produktu. Następnie wystarczy zadzwonić unscopeddo pozostałych 1% przypadków, co prawdopodobnie jest panelem administracyjnym.
pedrozath
215

W Rails 3:

foos = Foo.unscoped.where(:baz => baz)
Vincent
źródło
58
Ma to efekt uboczny, jeśli Post has_many Comment, Post.first.comments.unscoped zwraca WSZYSTKIE komentarze.
Enrico Carlesso,
3
To naprawdę mnie zepsuło na chwilę. Zwłaszcza jeśli def self.random; unscoped.order('rand()'); endumieścisz to w metodzie klasowej, takiej jak: unscoped usuwa WSZYSTKIE sql przed nim, a nie tylko to, co jest wymienione w default_scope. Chociaż technicznie poprawna odpowiedź, uważaj, używającunstopped
Schneems
7
OSTRZEŻENIE! Unscoped NIE usuwa tylko default_scope, zostało to już powiedziane w innym komentarzu, ale może naprawdę zepsuć rzeczy.
dsimard
15
Dobrą praktyczną zasadą jest tylko unscopedwtedy, gdy może bezpośrednio podążać za modelem, np. Foo.unscoped.blah()Jest w porządku, ale nigdy Foo.blah().unscoped.
Grant Birchmeier
stackoverflow.com/questions/1834159/… działa wokół efektu ubocznego wspomnianego przez Enrico
wbharding
107

Jeśli wystarczy zmienić kolejność zdefiniowaną w default_scope, możesz skorzystać z reordermetody .

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

Foo.reorder('created_at asc')

uruchamia następujący SQL:

SELECT * FROM "foos" ORDER BY created_at asc
GuiGS
źródło
3
Wskazówka: zdefiniuj zakres, jak scope :without_default_order, -> { reorder("") }i możesz robić takie rzeczy, jak Foo.without_default_order.order("created_at ASC")W niektórych sytuacjach lepiej czyta (może nie ta dokładna sytuacja, ale miałem taką).
Henrik N
Zmiana kolejności zrobiła to za mnie. Wielkie dzięki!
Andre Zimpel
49

Ponieważ 4.1możesz użyć ActiveRecord::QueryMethods#unscopedo walki z domyślnym zakresem:

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

Jest to obecnie możliwe do unscoperzeczy, takich jak: :where, :select, :group, :order, :lock, :limit, :offset, :joins, :includes, :from, :readonly, :having.

Ale nadal unikaj używania of, default_scopejeśli możesz . To dla twojego dobra.

jibiel
źródło
Ta odpowiedź powinna być wyższa
Stephen Corwin
5

Rails 3 default_scope nie wydaje się być nadpisywany, tak jak w Rails 2.

na przykład

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"

W mojej aplikacji używającej PostgreSQL, kolejność w domyślnym zakresie WINS. Usuwam wszystkie moje default_scope i koduję je jawnie wszędzie.

Pitfall Rails3!

vanboom
źródło
1
Musisz użyćBar.foos.reorder(:created_at => :asc)
Ivan Stana
5

Z Rails 3+ możesz używać kombinacji bez zakresu i scalania:

# 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
źródło
To też zadziałało dla mnie, najpierw zadzwoniłem bez zakresu (Rails 4.2):User.unscoped.where(email: "[email protected]")
Nick
3

Na Railsach 5.1+ (a może wcześniej, ale testowałem to na 5.1) możliwe jest wyłączenie określonej kolumny, co jest idealnym rozwiązaniem do usuwania a default_scopew sposób, który może być użyty wewnątrz nazwanego zakresu. W przypadku programów operacyjnych default_scope,

Foo.unscope(where: :bar)

Lub

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

Oba spowodują zapytanie sql, które nie stosuje oryginalnego zakresu, ale stosuje wszelkie inne warunki, które zostaną scalone w arel.

wbharding
źródło
2

Cóż, zawsze możesz użyć starej listy ulubionych find_by_sqlz pełnym zapytaniem. Na przykład: Model.find_by_sql ("SELECT * FROM models WHERE id = 123")

Ady Rosen
źródło