Bir nesne dizisini ActiveRecord :: Relation'a dönüştürme


105

Bir dizi nesnem var, buna bir Indicator. def self.subjectsBu dizide Gösterge sınıfı yöntemlerini ( çeşit, kapsam, vb.) Çalıştırmak istiyorum . Bir grup nesne üzerinde sınıf yöntemlerini çalıştırmayı bilmemin tek yolu, bunların bir ActiveRecord :: Relation olmasını sağlamaktır. Bu yüzden bir to_indicatorsyöntem eklemeye başvurmaya başladım Array.

def to_indicators
  # TODO: Make this less terrible.
  Indicator.where id: self.pluck(:id)
end

Bazen sınıf yöntemleri içinde sonuçları filtrelemek için bu kapsamlardan epeyce birkaçını zincirledim. Bu nedenle, bir ActiveRecord :: Relation'da bir yöntem çağırsam da, o nesneye nasıl erişeceğimi bilmiyorum. İçeriğine ancak aracılığıyla allulaşabilirim. Ama allbir Dizidir. Bu yüzden o diziyi ActiveRecord :: Relation'a dönüştürmem gerekiyor. Örneğin bu, yöntemlerden birinin parçasıdır:

all.to_indicators.applicable_for_bank(id).each do |indicator|
  total += indicator.residual_risk_for(id)
  indicator_count += 1 if indicator.completed_by?(id)
end

Sanırım bu iki soruya indirgeniyor.

  1. Bir nesne dizisini ActiveRecord :: Relation'a nasıl dönüştürebilirim? Tercihen whereher seferinde yapmadan .
  2. def self.subjectsActiveRecord :: Relation üzerinde bir tür yöntemi çalıştırırken , bu ActiveRecord :: Relation nesnesinin kendisine nasıl erişebilirim?

Teşekkürler. Bir şeyi açıklığa kavuşturmam gerekirse bana bildirin.


3
Bu diziyi tekrar bir ilişkiye dönüştürmeye çalışmanın tek nedeni, onu üzerinden elde etmiş olmanızsa .all, .scopedAndrew Marshall'ın cevabının belirttiği gibi kullanın (Raylar 4'te bununla birlikte çalışacaktır .all). Bir diziyi bir ilişkiye dönüştürmeye ihtiyaç duyuyorsanız, bir yerde yanlış
yaptınız

Yanıtlar:


46

Bir nesne dizisini ActiveRecord :: Relation'a nasıl dönüştürebilirim? Tercihen her seferinde bir yerde yapmadan.

Bir İlişki yalnızca bir SQL sorgusu için oluşturucu olduğundan ve yöntemleri gerçek veriler üzerinde çalışmadığından, Diziyi ActiveRecord :: Relation'a dönüştüremezsiniz.

Ancak, istediğiniz şey bir ilişki ise o zaman:

  • ActiveRecord 3.x için, arama yapmayın allve onun yerine scoped, allsize bir Dizide verecek olan aynı kayıtları temsil eden bir İlişkiyi geri verecektir.

  • ActiveRecord 4.x için, allbir İlişki döndüren basitçe çağırın .

def self.subjectsActiveRecord :: Relation üzerinde bir tür yöntemi çalıştırırken , bu ActiveRecord :: Relation nesnesinin kendisine nasıl erişebilirim?

Yöntem bir Relation nesnesinde çağrıldığında self, ilişkidir (içinde tanımlandığı model sınıfının aksine).


1
Çözüm için aşağıdaki @Marco Prins'e bakın.
Justin

raylar içinde .push kaldırılan yöntemi hakkında neler 5
Jaswinder

@GstjiSaini Bahsettiğiniz tam yöntemden emin değilim, lütfen belge veya kaynak bağlantısını sağlayın. Yine de, eğer kullanımdan kaldırılırsa, yakında ortadan kalkacağı için pek geçerli bir çözüm değildir.
Andrew Marshall

Ve bu yüzden yapabilirsin class User; def self.somewhere; where(location: "somewhere"); end; end, o zamanUser.limit(5).somewhere
Dorian

OP'yi gerçekten aydınlatan tek cevap budur. Soru, ActiveRecord'un nasıl çalıştığına dair hatalı bilgileri ortaya çıkarır. @Justin, sırf bunların üzerine haritalamak ve başka bir gereksiz sorgu oluşturmak için nesnelerin dizilerini etrafta geçirmenin neden kötü olduğu konusundaki anlayış eksikliğini pekiştiriyor.
james2m

162

Bir dizi nesneyi arrbunun gibi bir ActiveRecord :: İlişkisine dönüştürebilirsiniz (nesnelerin hangi sınıf olduğunu bildiğinizi varsayarsak, muhtemelen bunu yaparsınız)

MyModel.where(id: arr.map(&:id))

Kullanmak zorundasın, kullanmaktan whereçekinmemen gereken kullanışlı bir araç. Ve şimdi bir diziyi bir ilişkiye dönüştüren tek satırlık bir programınız var.

map(&:id)nesneler dizinizi yalnızca kimliklerini içeren bir diziye çevirir. Ve bir diziyi bir where cümlesine geçirmek IN, şuna benzer bir SQL ifadesi oluşturacaktır :

SELECT .... WHERE `my_models`.id IN (2, 3, 4, 6, ....

Dizinin sıralamasının kaybolacağını unutmayın - Ancak amacınız yalnızca bu nesnelerin koleksiyonunda bir sınıf yöntemi çalıştırmak olduğundan, bunun bir sorun olmayacağını varsayıyorum.


3
Aferin, tam olarak ihtiyacım olan şey. Bunu, modeldeki herhangi bir öznitelik için kullanabilirsiniz. benim için mükemmel çalışıyor.
nfriend21

7
Yapabilecekken neden gerçek SQL'i kendiniz oluşturasınız where(id: arr.map(&:id))? Ve kesinlikle konuşursak, bu diziyi bir ilişkiye dönüştürmez, bunun yerine nesnelerin yeni örneklerini (ilişki gerçekleştiğinde) dizideki halihazırda bellekte bulunan örneklerden farklı öznitelik değerlerine sahip olabilecek bu ID'lerle alır.
Andrew Marshall

7
Bu yine de sıralamayı kaybeder.
Velizar Hristov

1
@VelizarHristov Bunun nedeni artık bir ilişki olmasıdır, yalnızca bir sütunla sıralanabilir ve istediğiniz şekilde değil. İlişkiler, büyük veri kümelerini işlemek için daha hızlıdır ve bazı ödünleşmeler olacaktır.
Marco Prins

8
Çok verimsiz! Zaten hafızanızda bulunan nesnelerin bir koleksiyonunu, erişmek için bir veritabanı sorgusu yapacaklarınıza dönüştürdünüz. Dizi üzerinde yinelemek istediğiniz sınıf yöntemlerini yeniden düzenlemeye bakardım.
james2m

5

Eh, benim durumumda, ben gerek :: ActiveRecord için İlişkisi nesneleri dizisini dönüştürme yanı sıra sıralama belirli bir sütunda (örneğin id) ile bunları. MySQL kullandığım için alan işlevi yardımcı olabilir.

MyModel.where('id in (?)',ids).order("field(id,#{ids.join(",")})") 

SQL şöyle görünür:

SELECT ... FROM ... WHERE (id in (11,5,6,7,8,9,10))  
ORDER BY field(id,11,5,6,7,8,9,10)

MySQL alan işlevi


Bunun PostgreSQL ile çalışan bir sürümü için, bu konu
armchairdj

0

ActiveRecord::Relation veritabanından veri alan veritabanı sorgusunu bağlar.

Diyelim ki mantıklı bir şekilde, aynı sınıftan nesnelerden oluşan bir dizimiz var, o zaman onları hangi sorgu ile bağlamamız gerekiyor?

Koştuğumda

users = User.where(id: [1,3,4,5])
  User Load (0.6ms)  SELECT `users`.* FROM `users` WHERE `users`.`id` IN (1, 3, 4, 5)  ORDER BY created_at desc

İşte yukarıda, usersdönüş Relationnesnesi ancak bağlandığı veritabanı sorgu arkasında ve bunu görebilirsiniz,

users.to_sql
 => "SELECT `users`.* FROM `users` WHERE `users`.`id` IN (1, 3, 4, 5)  ORDER BY created_at desc"

Dolayısıyla ActiveRecord::Relation, sql sorgusundan bağımsız olan nesneler dizisinden dönmek mümkün değildir .


0

Her şeyden önce, bu sihirli bir değnek DEĞİLDİR. Deneyimlerime göre, ilişkiye geçmenin bazen alternatiflerden daha kolay olduğunu buldum. Bu yaklaşımı çok idareli ve yalnızca alternatifin daha karmaşık olacağı durumlarda kullanmaya çalışıyorum.

Burada söylenen benim çözümüm, Arraysınıfı genişlettim

# lib/core_ext/array.rb

class Array

  def to_activerecord_relation
    return ApplicationRecord.none if self.empty?

    clazzes = self.collect(&:class).uniq
    raise 'Array cannot be converted to ActiveRecord::Relation since it does not have same elements' if clazzes.size > 1

    clazz = clazzes.first
    raise 'Element class is not ApplicationRecord and as such cannot be converted' unless clazz.ancestors.include? ApplicationRecord

    clazz.where(id: self.collect(&:id))
  end
end

Bir kullanım örneği olacaktır array.to_activerecord_relation.update_all(status: 'finished'). Şimdi onu nerede kullanacağım?

Bazen ActiveRecord::Relation, örneğin tamamlanmamış öğeleri dışarıda bırakmanız gerekir . Bu durumlarda en iyisi kapsamı kullanmaktır elements.not_finishedve yine de tutarsınız ActiveRecord::Relation.

Ancak bazen bu durum daha karmaşıktır. Bitmemiş, son 4 haftada üretilmiş ve incelenmiş tüm parçaları çıkarın. Yeni kapsamlar oluşturmaktan kaçınmak için bir diziye filtre uygulayabilir ve ardından geri dönüştürebilirsiniz. Yine de bir sorguya göre arama yaptığı için hızlı bir şekilde DB'ye idbir sorgu yaptığınızı unutmayın.

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.