Boş bir ActiveRecord ilişkisi nasıl döndürülür?


246

Eğer bir lambda ile bir kapsamı varsa ve argümanın değerine bağlı olarak bir argüman alırsa, herhangi bir eşleşme olmayacağını biliyor olabilirim, ama yine de boş bir dizi değil, bir ilişki döndürmek istiyorum:

scope :for_users, lambda { |users| users.any? ? where("user_id IN (?)", users.map(&:id).join(',')) : [] }

Gerçekten istediğim, "zincirleme", yine de zincirleme bir ilişki döndürür, ancak kısa devre sorgusu sonuçlanan bir "none" yöntemidir.


Sorguyu yalnızca izin verirseniz, çalıştırın bir ilişki döndürür: User.where ('id in (?)', []). Class => ActiveRecord :: İlişki. Sorgudan tamamen kaçınmaya mı çalışıyorsunuz?
Brian Deterling

1
Doğru. İdeal olarak, herhangi bir eşleşme olamayacağını biliyorsanız, sorgu tamamen önlenebilir. Ben sadece ActiveRecord :: Base ekledi: "def self.none; nerede (: id => 0); end" İhtiyacım olan şey için iyi çalışıyor gibi görünüyor.
dzajic

1
> Sorgudan tamamen kaçınmaya mı çalışıyorsunuz? tamamen mantıklı olurdu, bunun için DB vurmak gerekir tür topal
dolzenko

Yanıtlar:


478

Şimdi Rails 4'te "doğru" bir mekanizma var:

>> Model.none 
=> #<ActiveRecord::Relation []>

14
Şimdiye kadar bu, 3.2 veya daha önceki bir süreye taşınmamıştır. Only edge (4.0)
Chris Bloom


2
Sadece Rails 4.0.5 ile denedim ve çalışıyor. Bu özellik Rails 4.0 sürümüne ulaştı.
Evolve

3
@AugustinRiedinger Model.scopedray 3'te aradığınızı yapıyor.
Tim Diggins

9
Rails 4.0.5 itibariyle Model.noneçalışmıyor. Gerçek model adlarınızdan birini kullanmanız gerekir , örneğin User.noneveya sahip olduğunuz şey.
Grant Birchmeier

77

"Kimlik" sütunu gerektirmeyen ve 0 kimliğine sahip bir satır olmayacağını varsaymayan daha taşınabilir bir çözüm:

scope :none, where("1 = 0")

Hala daha "doğru" bir yol arıyorum.


3
Evet, bu cevapların elimizden gelenin en iyisini olması gerçekten şaşırdım. ActiveRecord / Arel hala oldukça olgunlaşmamış olmalı. Ruby'de boş bir dizi oluşturmak için perambülasyonlardan geçmek zorunda kalsaydım gerçekten rahatsız olurdum. Aynı şey, temelde.
Purplejacket

Biraz hackish rağmen, bu ise Raylar 3.2 için doğru bir yol. Rails 4 için, @ steveh7'nin diğer cevabı için buraya bakınız: stackoverflow.com/a/10001043/307308
scarver2

scope :none, -> { where("false") }
17'de

43

Raylar 4'e geliyor

Rails 4'te ActiveRecord::NullRelationgibi çağrılardan zincirlenebilir bir ücret iade edilecektir Post.none.

Ne o ne de zincirleme yöntemler veritabanına sorgular üretmez.

Yorumlara göre:

Döndürülen ActiveRecord :: NullRelation İlişkiden miras alır ve Null Nesne desenini uygular. Tanımlanmış null davranışı olan bir nesnedir ve veritabanını sorgulamadan her zaman boş bir kayıt dizisi döndürür.

Kaynak koduna bakın .


42

"None" adlı bir kapsam ekleyebilirsiniz:

scope :none, where(:id => nil).where("id IS NOT ?", nil)

Bu size boş bir ActiveRecord :: İlişkisi verecektir.

Başlatıcıda ActiveRecord :: Base'e de ekleyebilirsiniz (isterseniz):

class ActiveRecord::Base
 def self.none
   where(arel_table[:id].eq(nil).and(arel_table[:id].not_eq(nil)))
 end
end

Böyle bir şey elde etmek için pek çok yol, ama kesinlikle bir kod tabanı tutmak için en iyi şey değil. Ben kapsamı kullandım: hiçbiri yeniden düzenleme ve boş bir ActiveRecord :: İlişkisi kısa bir süre için garanti gerektiğini bulmak.


14
where('1=2')biraz daha özlü olabilir
Marcin Raczkowski

10
Yeni 'doğru' cevaba gitmemeniz durumunda: Model.nonebunu nasıl yapacağınızdır.
Joe Essey

26
scope :none, limit(0)

Tehlikeli bir çözümdür çünkü kapsamınız zincirlenebilir.

User.none.first

ilk kullanıcıyı döndürür. Kullanımı daha güvenli

scope :none, where('1 = 0')

2
Bu doğru olanın kapsamıdır: hiçbiri, burada ('1 = 0') '. sayfa numaralandırmanız varsa diğeri başarısız olacaktır
Federico

14

Bunun diğer seçeneklere bakışını tercih ettiğimi düşünüyorum:

scope :none, limit(0)

Böyle bir şeye öncülük etmek:

scope :users, lambda { |ids| ids.present? ? where("user_id IN (?)", ids) : limit(0) }

Bunu tercih ederim. Neden where(false)işi yapmayacağından emin değilim - kapsamı değiştirmez.
aceofspades

4
Rails bu sorguya ekleneceğinden, zincirde veya daha sonra limit(0)çağırırsanız bunun geçersiz kılınacağına dikkat edin . .first.lastLIMIT 1
zykadelic

2
@aceofspades burada (false) çalışmaz (Rails 3.0) ama nerede ('false') çalışır. Muhtemelen şimdi umurumda değil 2013 :)
Ritchie

@Ritchie teşekkürler, o zamandan beri noneaşağıda belirtildiği gibi bir ilişkimiz olduğunu düşünüyorum .
aceofspades'ait

3

Kapsamlı kullanın:

kapsam: for_users, lambda {| kullanıcılar | users.any? ? nerede ("user_id IN (?)", users.map (&: id) .join (',')): kapsamlandırılmış}

Ancak, kodunuzu aşağıdakilerle de basitleştirebilirsiniz:

kapsam: for_users, lambda {| kullanıcılar | where (: user_id => users.map (&: id)) users.any ise? }

Boş bir sonuç istiyorsanız, bunu kullanın (if koşulunu kaldırın):

kapsam: for_users, lambda {| kullanıcılar | burada (: user_id => users.map (&: id))}

"Kapsamlı" veya sıfır döndürmek istediğimi elde etmez, bu da sonuçları sıfıra sınırlamaktır. "Kapsamlı" veya sıfır döndürmenin kapsam üzerinde hiçbir etkisi yoktur (bazı durumlarda yararlıdır, ancak benimkinde değil). Kendi cevabımı buldum (yukarıdaki yorumlara bakınız).
dzajic

Ben de boş bir sonuç döndürmek için basit bir çözüm ekledim :)
Pan Thomakos


1

Ayrıca varyantlar var, ancak bunların hepsi db

where('false')
where('null')

2
BTW Bunların dize olması gerektiğini unutmayın. where(false)veya where(nil)basitçe yok sayılır.
mahemoff
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.