J'ai un tableau d'objets, appelons-le un Indicator
. Je veux exécuter des méthodes de classe Indicator (celles de la def self.subjects
variété, des portées, etc.) sur ce tableau. La seule façon que je connais d'exécuter des méthodes de classe sur un groupe d'objets est de les avoir comme ActiveRecord :: Relation. Alors je to_indicators
finis par avoir recours à l'ajout d'une méthode à Array
.
def to_indicators
# TODO: Make this less terrible.
Indicator.where id: self.pluck(:id)
end
Parfois, j'enchaine plusieurs de ces portées pour filtrer les résultats, dans les méthodes de classe. Donc, même si j'appelle une méthode sur un ActiveRecord :: Relation, je ne sais pas comment accéder à cet objet. Je ne peux en obtenir le contenu qu'à travers all
. Mais all
est un tableau. Alors je dois convertir ce tableau en un ActiveRecord :: Relation. Par exemple, cela fait partie de l'une des méthodes:
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
Je suppose que cela se résume à deux questions.
- Comment puis-je convertir un tableau d'objets en ActiveRecord :: Relation? De préférence sans en faire à
where
chaque fois. - Lors de l'exécution d'une
def self.subjects
méthode de type sur un ActiveRecord :: Relation, comment accéder à cet objet ActiveRecord :: Relation lui-même?
Merci. Si j'ai besoin de clarifier quelque chose, faites-le moi savoir.
la source
.all
, utilisez simplement.scoped
comme l'indique la réponse d'Andrew Marshall (bien que dans les rails 4, cela fonctionne avec.all
). Si vous avez besoin de transformer un tableau en une relation, vous vous êtes trompé quelque part ...Réponses:
Vous ne pouvez pas convertir un Array en ActiveRecord :: Relation car une Relation est juste un générateur pour une requête SQL et ses méthodes ne fonctionnent pas sur des données réelles.
Cependant, si vous voulez une relation, alors:
pour ActiveRecord 3.x, n'appelez pas
all
et appelez à la placescoped
, ce qui rendra une Relation qui représente les mêmes enregistrements que ceuxall
qui vous donneraient dans un tableau.pour ActiveRecord 4.x, appelez simplement
all
, ce qui renvoie une relation.Lorsque la méthode est appelée sur un objet Relation,
self
est la relation (par opposition à la classe de modèle dans laquelle elle est définie).la source
class User; def self.somewhere; where(location: "somewhere"); end; end
, alorsUser.limit(5).somewhere
Vous pouvez convertir un tableau d'objets
arr
en ActiveRecord :: Relation comme ceci (en supposant que vous sachiez de quelle classe sont les objets, ce que vous faites probablement)Vous devez cependant l'utiliser
where
, c'est un outil utile que vous ne devriez pas hésiter à utiliser. Et maintenant, vous avez une ligne unique convertissant un tableau en une relation.map(&:id)
transformera votre tableau d'objets en un tableau contenant uniquement leurs identifiants. Et passer un tableau à une clause where générera une instruction SQL avecIN
qui ressemble à quelque chose comme:Gardez à l'esprit que l'ordre du tableau sera perdu - Mais comme votre objectif est uniquement d'exécuter une méthode de classe sur la collection de ces objets, je suppose que ce ne sera pas un problème.
la source
where(id: arr.map(&:id))
? Et à proprement parler, cela ne convertit pas le tableau en une relation, mais obtient à la place de nouvelles instances des objets (une fois la relation réalisée) avec ces ID qui peuvent avoir des valeurs d'attribut différentes de celles des instances déjà en mémoire dans le tableau.Eh bien, dans mon cas, j'ai besoin de convertir un tableau d'objets en ActiveRecord :: Relation ainsi que de les trier avec une colonne spécifique (id par exemple). Puisque j'utilise MySQL, la fonction de champ pourrait être utile.
Le SQL ressemble à:
Fonction de champ MySQL
la source
ActiveRecord::Relation
lie la requête de base de données qui récupère les données de la base de données.Supposons que cela ait du sens, nous avons un tableau avec des objets de même classe, alors avec quelle requête nous supposons les lier?
Quand je cours,
Ici ci-dessus,
users
retournez l'Relation
objet mais lie la requête de base de données derrière lui et vous pouvez l'afficher,Il n'est donc pas possible de retourner à
ActiveRecord::Relation
partir d'un tableau d'objets indépendant de la requête SQL.la source
Tout d'abord, ce n'est PAS une solution miracle. D'après mon expérience, j'ai trouvé que la conversion à la relation est parfois plus facile que les alternatives. J'essaie d'utiliser cette approche avec parcimonie et uniquement dans les cas où l'alternative serait plus complexe.
Cela étant dit voici ma solution, j'ai étendu la
Array
classeUn exemple d'utilisation serait
array.to_activerecord_relation.update_all(status: 'finished')
. Maintenant où est-ce que je l'utilise?Parfois, vous devez filtrer,
ActiveRecord::Relation
par exemple retirer les éléments non terminés. Dans ces cas, le mieux est d'utiliser la portéeelements.not_finished
et vous conserverez toujoursActiveRecord::Relation
.Mais parfois, cette condition est plus complexe. Retirez tous les éléments qui ne sont pas finis, qui ont été produits au cours des 4 dernières semaines et qui ont été inspectés. Pour éviter de créer de nouvelles étendues, vous pouvez filtrer en tableau, puis reconvertir. Gardez à l'esprit que vous faites toujours une requête à DB, rapide car il recherche par
id
mais toujours une requête.la source