it-swarm.com.de

Konvertieren eines Arrays von Objekten in ActiveRecord :: Relation

Ich habe ein Array von Objekten, nennen wir es eine Indicator. Ich möchte Indicator-Klassenmethoden (die der def self.subjects-Sorte, Gültigkeitsbereiche usw.) für dieses Array ausführen. Die einzige Möglichkeit, Klassenmethoden für eine Gruppe von Objekten auszuführen, besteht darin, sie als ActiveRecord :: Relation zu verwenden. Am Ende greife ich darauf zurück, eine to_indicators-Methode zu Array hinzuzufügen.

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

Manchmal verkettete ich einige dieser Bereiche, um die Ergebnisse innerhalb der Klassenmethoden zu filtern. Obwohl ich eine Methode für ActiveRecord :: Relation aufrufe, weiß ich nicht, wie ich auf dieses Objekt zugreifen kann. Ich kann nur über all zum Inhalt gelangen. all ist jedoch ein Array. Also muss ich dieses Array in ein ActiveRecord :: Relation konvertieren. Dies ist zum Beispiel Teil einer der Methoden:

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

Ich denke, das hat zwei Fragen.

  1. Wie kann ich ein Array von Objekten in ActiveRecord :: Relation konvertieren? Vorzugsweise, ohne jedes Mal eine where zu machen.
  2. Wie kann ich bei der Ausführung einer def self.subjects-Methode für ein ActiveRecord :: Relation-Objekt auf dieses ActiveRecord :: Relation-Objekt selbst zugreifen?

Vielen Dank. Wenn ich etwas klären muss, lassen Sie es mich wissen.

88
Nathan

Wie kann ich ein Array von Objekten in ActiveRecord :: Relation konvertieren? Vorzugsweise ohne dabei jedes Mal ein Wo zu tun.

Sie können ein Array nicht in eine ActiveRecord :: Relation konvertieren, da eine Relation nur ein Builder für eine SQL-Abfrage ist und ihre Methoden nicht mit den tatsächlichen Daten arbeiten.

Wenn Sie jedoch eine Beziehung wollen, gilt Folgendes:

  • rufen Sie für ActiveRecord 3.x nicht all auf, sondern rufen Sie stattdessen scoped auf. Dies gibt eine Relation zurück, die die gleichen Datensätze darstellt, die all Ihnen in einem Array geben würde.

  • für ActiveRecord 4.x rufen Sie einfach all auf, was eine Relation zurückgibt.

Wie kann ich bei der Ausführung einer def self.subjects-Methode für ein ActiveRecord :: Relation-Objekt auf dieses ActiveRecord :: Relation-Objekt selbst zugreifen?

Wenn die Methode für ein Beziehungsobjekt aufgerufen wird, ist self die Beziehung (im Gegensatz zu der Modellklasse, in der sie definiert ist).

42
Andrew Marshall

Sie können ein Array von Objekten arr in eine ActiveRecord :: Relation wie folgt konvertieren (vorausgesetzt, Sie wissen, welche Klasse die Objekte sind und was Sie wahrscheinlich tun).

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

Sie müssen jedoch where verwenden, es ist ein nützliches Werkzeug, das Sie nicht ungern verwenden sollten. Und jetzt haben Sie einen Einzeiler, der ein Array in eine Relation konvertiert.

map(&:id) verwandelt Ihr Array von Objekten in ein Array, das nur deren IDs enthält. Wenn Sie ein Array an eine where-Klausel übergeben, wird eine SQL-Anweisung mit IN generiert, die etwa wie folgt aussieht:

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

Denken Sie daran, dass die Reihenfolge des Arrays verloren geht - Da Sie jedoch nur eine Klassenmethode für die collection dieser Objekte ausführen möchten, gehe ich davon aus, dass dies kein Problem darstellt.

140
Marco Prins

In meinem Fall muss ich ein Array von Objekten in ActiveRecord :: Relation konvertieren sowie Sortierung mit einer bestimmten Spalte (id zum Beispiel) konvertieren. Da ich MySQL verwende, könnte die Feldfunktion hilfreich sein.

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

Die SQL sieht so aus:

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

MySQL-Feldfunktion

4
Xingcheng Xia

ActiveRecord::Relation bindet die Datenbankabfrage, die Daten aus der Datenbank abruft.

Angenommen, es macht Sinn, Wir haben ein Array mit Objekten derselben Klasse, und mit welcher Abfrage nehmen wir an, dass wir sie binden?

Wenn ich renne,

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

Hier oben, usersreturn Relation, bindet jedoch die Datenbankabfrage dahinter und Sie können sie anzeigen

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

Daher ist es nicht möglich, ActiveRecord::Relation aus einem Array von Objekten zurückzugeben, das unabhängig von der SQL-Abfrage ist.

0
ray

Erstens ist dies KEINE Silberkugel. Aus meiner Erfahrung heraus stellte ich fest, dass das Konvertieren in eine Beziehung manchmal einfacher ist als Alternativen. Ich versuche, diesen Ansatz sehr sparsam und nur in Fällen anzuwenden, in denen die Alternative komplexer wäre.

Das hier Gesagte ist meine Lösung, ich habe die Klasse Array erweitert

# 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

Ein Anwendungsbeispiel wäre array.to_activerecord_relation.update_all(status: 'finished'). Wo verwende ich es jetzt?

Manchmal müssen Sie ActiveRecord::Relation herausfiltern, um beispielsweise nicht abgeschlossene Elemente herauszunehmen. In diesen Fällen ist es am besten, scope elements.not_finished zu verwenden und ActiveRecord::Relation beizubehalten.

Aber manchmal ist diese Bedingung komplexer. Nehmen Sie alle Elemente heraus, die nicht fertig sind und die in den letzten 4 Wochen hergestellt und geprüft wurden. Um das Erstellen neuer Bereiche zu vermeiden, können Sie in ein Array filtern und dann zurückkonvertieren. Denken Sie daran, dass Sie immer noch eine Abfrage an DB durchführen, schnell, da nach id gesucht wird, aber immer noch eine Abfrage.

0
Haris Krajina