web-dev-qa-db-fra.com

Obtenir des attributs spécifiques d'un modèle ActiveRecord

Disons que j'ai un modèle User avec des attributs :id, :first_name, :last_name, and :email. Dans mon application, les utilisateurs invités ne devraient pas voir User's email and last_name. Je sais comment sélectionner des valeurs spécifiques si je veux une liste d'utilisateurs mais je ne sais pas comment obtenir les attributs spécifiques d'un utilisateur spécifique, comme

User.find_by(id: 5).select(:id, :first_name).

Une solution à cela est à l'utilisateur

User.find_by(id: 5).attributes.slice('id', 'first_name') 

mais alors je reçois un hachage au lieu d'un enregistrement AR. je pourrais faire

User.select(:id, :first_name).find_by(id: 1) 

mais la chose que je ne sais pas quels filtres je devrais filtrer puisque je dois d'abord connaître l'utilisateur.

Pourriez-vous m'aider?

38
vasilakisfil

Ce que Choco a dit va marcher si vous voulez un tableau et non une instance d’enregistrement actif. Si vous voulez une instance d'enregistrement active, faites:

User.where(id: 5).select(:id, :first_name).take 

Voici quelques informations supplémentaires. Je ne sais pas à quel point vous savez ou ne savez pas, alors je suppose que vous en savez moins que plus.

Je suppose que vous réalisez que ce que vous faites ci-dessus est le chaînage de méthodes - vous appelez une méthode, puis vous appelez une autre méthode sur le résultat de la première méthode. Par exemple:

User.find_by(id: 5).attributes.slice('id', 'first_name')

Vous appelez la méthode find_by_id Dans la classe User. Cette méthode renverra une instance de la classe User (c'est-à-dire une instance d'enregistrement active). Cette instance a une méthode appelée attributes, que vous appelez. La méthode attributes renvoie une instance de la classe Hash, représentant les champs de la précédente instance d'enregistrement active. Hash a une méthode slice, que vous appelez à son tour. La méthode slice renvoie une autre instance de Hash, contenant le sous-ensemble de champs que vous avez spécifié.

Il est donc clair que lorsque vous chaînez des méthodes, vous n'appelez pas chaque méthode suivante sur la même chose, mais sur la valeur de retour de la méthode précédente de la chaîne.

OK, donc avec ça à l'écart:

Toutes les méthodes find sont destinées à interroger la base de données et à renvoyer une seule instance d'un modèle Active Record ou un tableau simple Ruby contenant plusieurs instances de modèles Active Record.

Beaucoup d'autres méthodes - select, where, etc., NE frappent PAS immédiatement la base de données et ne sont PAS destinées à renvoyer une instance d'enregistrement active. Ils renvoient tous une instance de ActiveRecord::Relation. Un ActiveRecord::Relation Est un peu comme un groupe d'enregistrements correspondant à certaines conditions - et vous pouvez ajouter des conditions supplémentaires. C'est comme une "requête de base de données en attente" ou une "requête de base de données qui peut ou peut ne pas avoir encore eu lieu".

Lorsque vous appelez une méthode telle que where, order ou select sur ActiveRecord::Relation, Elle renvoie une instance de ActiveRecord::Relation, Ce qui signifie que vous 'ai une instance de la même classe et peut chaîner des méthodes de cette classe, par exemple: User.where(name: 'Fred').select(:id, :name).order('name ASC')

Votre instance de ActiveRecord::Relation Sera très intelligente et ne vous ennuierez pas de frapper la base de données jusqu'à ce que vous appeliez une méthode indiquant clairement que vous voulez les enregistrements maintenant, tels que each, all, take, etc.

Donc, je suis allé bien plus longtemps que prévu ici, donc je vais conclure. Voici le code que j'ai mentionné pour la première fois, avec une explication détaillée très commentée ci-dessous:

User.where(id: 5).select(:id, :first_name).take

le décomposer:

my_relation = User.where(id: 5)
# The database will not have been hit yet - my_relation 
# is a query waiting to happen

my_second_relation = my_relation.select(:id, :first_name)
# The database has STILL not been hit.
# the relation now contains both the conditions 
# from the previous my_relation, and the new 'select'
# criteria we specified

my_user = my_second_relation.take
# OK, 'take' means we want the first record
# matching all our criteria,
# so my_second_relation will hit the database 
# to get it, and then return an Active Record 
# instance (ie, an instance of the User class)

Wow ... je suis allé plus longtemps que prévu. J'espère que certaines d'entre elles ont été utiles!

95
joshua.paling

Essaye ça:

User.where(id: 5).pluck(:id, :first_name) 

il retourne un tableau contenant les valeurs id et first_name

24
Choco

Cela fonctionnera aussi bien

User.select(:id, :first_name).find(5)

4
tkhuynh