J'ai un modèle de profil. Profil has_one Utilisateur. Le modèle d'utilisateur a un email de champ. Quand j'appelle
Profile.some_scope.includes(:user)
il appelle
SELECT users.* FROM users WHERE users.id IN (some ids)
Mais mon modèle utilisateur comporte de nombreux champs que je n'utilise pas dans le rendu. Est-il possible de charger uniquement les emails des utilisateurs? Donc, SQL devrait ressembler à
SELECT users.email FROM users WHERE users.id IN (some ids)
Les rails n'ont pas la possibilité de passer les options pour include query. Mais nous pouvons passer ces paramètres avec la déclaration d'association sous le modèle.
Pour votre scénario, vous devez créer un nouveau modèle d'association avec des utilisateurs sous le modèle de profil, comme ci-dessous.
belongs_to :user_only_fetch_email, :select => "users.id, users.email", :class_name => "User"
je viens de créer une autre association, mais elle pointe uniquement vers le modèle utilisateur. Donc, votre requête sera,
Profile.includes(:user_only_fetch_email)
ou
Profile.includes(:user_only_fetch_email).find(some_profile_ids)
Si vous souhaitez sélectionner des attributs spécifiques, vous devez utiliser joins
plutôt que includes
.
À partir de cet asciicast :
l’option d’inclusion ne fonctionne pas vraiment avec l’option de sélection car nous n’avons pas le contrôle sur la façon dont la première partie de l’instruction SELECT est générée. Si vous avez besoin de contrôler les champs de la commande SELECT, vous devez utiliser des jointures sur inclure.
Utilisation de joins
:
Profile.some_scope.joins(:users).select("users.email")
Vous avez besoin d'appartenir au modèle.
Pour une association simple:
belongs_to :user_restricted, -> { select(:id, :email) }, class_name: 'User'
Pour une association polymorphe (par exemple, :commentable
):
belongs_to :commentable_restricted, -> { select(:id, :title) }, polymorphic: true, foreign_type: :commentable_type, foreign_key: :commentable_id
Vous pouvez choisir le nom belongs_to
de votre choix. Pour les exemples donnés ci-dessus, vous pouvez les utiliser comme Article.featured.includes(:user_restricted)
, Comment.recent.includes(:commentable_restricted)
etc.
Je voulais cette fonctionnalité moi-même, alors merci de l’utiliser. Incluez cette méthode dans votre classe
#ACCEPTS args au format de chaîne "ASSOCIATION_NAME: COLUMN_NAME-COLUMN_NAME"
def self.includes_with_select(*m)
association_arr = []
m.each do |part|
parts = part.split(':')
association = parts[0].to_sym
select_columns = parts[1].split('-')
association_macro = (self.reflect_on_association(association).macro)
association_arr << association.to_sym
class_name = self.reflect_on_association(association).class_name
self.send(association_macro, association, -> {select *select_columns}, class_name: "#{class_name.to_sym}")
end
self.includes(*association_arr)
end
Et vous pourrez appeler comme: Contract.includes_with_select('user:id-name-status', 'confirmation:confirmed-id')
, et il sélectionnera les colonnes spécifiées.
Rails ne prend pas en charge la sélection de colonnes spécifiques lorsque includes
. Vous savez, c'est juste lazy load
.
Il utilise le module ActiveRecord :: Associations :: Preloader pour charger les données associées avant que les données ne soient réellement utilisées. Par la méthode :
def preload(records, associations, preload_scope = nil)
records = Array.wrap(records).compact
if records.empty?
[]
else
records.uniq!
Array.wrap(associations).flat_map { |association|
preloaders_on association, records, preload_scope
}
end
end
preload_scope
, le troisième paramètre de preload
, est un moyen de sélectionner des colonnes. Mais ne peut plus charger paresseux .
relation = Profile.where(id: [1,2,3])
user_columns = {:select=>[:updated_at, :id, :name]}
preloader = ActiveRecord::Associations::Preloader.new
preloader.preload(relation, :user, user_columns)
Il sélectionnera les colonnes spécifiées que vous avez passées. Mais, il ne s'agit que d'une association. Vous devez créer un correctif pour ActiveRecord::Associations::Preloader
afin de prendre en charge le chargement simultané de plusieurs associations complexes.
En utilisant l'exemple de Mohanaj, vous pouvez faire ceci:
belongs_to :user_only_fetch_email, -> { select [:id, :email] }, :class_name => "User"