Specifieke attributen ophalen uit een ActiveRecord-model

Stel dat ik een User-model heb met attributen :id, :first_name, :last_name, and :email. In mijn applicatie zouden gastgebruikers User's email and last_nameniet moeten zien. Ik weet hoe ik specifieke waarden moet selecteren als ik een lijst met gebruikers wil, maar ik weet niet hoe ik specifieke attributen van een specifieke gebruiker kan krijgen, zoals

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

Een oplossing hiervoor is de gebruiker

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

maar dan krijg ik een hash in plaats van een AR-record. Ik zou kunnen doen

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

maar het ding dat ik niet weet welke filters ik moet filteren, omdat ik de gebruiker eerst moet kennen.

Kun je me helpen?


Antwoord 1, autoriteit 100%

Wat Choco zei, zal werken als je een array wilt en geen actieve recordinstantie. Als u een actieve recordinstantie wilt, doet u het volgende:

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

Hier is wat meer informatie. Ik weet niet zeker hoeveel je wel/niet weet, dus ik ga ervan uit dat je eerder minder dan meer weet.

Ik neem aan dat je je realiseert wat je hierboven doet, methode chaining is – dat wil zeggen, je roept een methode aan en roept vervolgens een andere methode aan op het resultaat van de eerste methode. Bijvoorbeeld:

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

Je roept de methode find_by_idaan in de klasse User. Die methode retourneert een instantievan de klasse User(dwz een actieve recordinstantie). Die instantie heeft een methode genaamd attributes, die je aanroept. De methode attributesretourneert een instantie van de klasse Hash, die de velden in de vorige actieve recordinstantie vertegenwoordigt. Hashheeft een slicemethode, die je beurtelings aanroept. De methode sliceretourneert een andere instantie van Hash, die de subset van velden bevat die u hebt opgegeven.

Dus het concept om duidelijk te zijn, is dat wanneer je methoden koppelt, je niet elke volgende methode op hetzelfde aanroept – je roept het op de geretourneerde waarde van de vorige methode in de keten.

OK, dus met dat uit de weg:

Alle find-methoden zijn bedoeld om de database te doorzoeken en een enkele instantie van een Active Record-model te retourneren, of een gewone ruby-array die meerdere instanties van Active Record-modellen bevat.

Veel andere methoden – select, where, enz., komen NIET meteen in de database en zijn NIET bedoeld om een ​​actieve recordinstantie te retourneren. Ze retourneren allemaal een instantie van ActiveRecord::Relation. Een ActiveRecord::Relationis een beetje als een potentiële groep records die aan bepaalde voorwaarden voldoen – en u kunt er extra voorwaarden aan toevoegen. Het is als een ‘databasequery die wacht om te gebeuren’, of een ‘databasequery die al dan niet is gebeurd’.

Als je een methode aanroept zoals where, orderof selectop ActiveRecord::Relation, retourneert een instantie van ActiveRecord::Relation, wat betekent dat u een instantie van dezelfde klasse hebt en methoden uit die klasse netjes kunt koppelen, bijvoorbeeld: User.where(name: 'Fred').select(:id, :name).order('name ASC')

Uw instantie van ActiveRecord::Relationzal erg slim zijn en zal niet de moeite nemen om de database te raken totdat u een methode aanroept die duidelijk aangeeft dat u de records nu wilt – zoals each, all, take, enz.

Dus ik ben hier veel langer onderweg dan gepland, dus ik zal afronden. Hier is de code die ik voor het eerst noemde, met een afgebroken, zwaar becommentarieerde uitleg hieronder:

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

het opsplitsen:

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)

Wauw… ik ging langer door dan verwacht. Ik hoop dat iets ervan nuttig was!


Antwoord 2, autoriteit 28%

Probeer dit:

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

het retourneert een array met id- en first_name-waarden


Antwoord 3, autoriteit 6%

Dit zal ook werken

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


Antwoord 4, autoriteit 2%

Had zo’n vereiste, maar wilde iets meer generieks dat gewoon een lijst met methodenamen en waarden uit een model zou verzamelen (inclusief attributen). Dus heb ik deze methode aan mijn model toegevoegd:

 def attributes_from_keys(*keys)
    keys.inject({}) do |hash_to_return, key|
      hash_to_return.merge(key => send(key))
    end
  end

Noem deze methode dan aan met:

MyModel.find(1).attributes_from_keys(:id, :name, :age)

retouren

{id: 1, name: 'Lawrence', age: 23}

Other episodes