Get specific attributes from an ActiveRecord model

Ruby on-RailsSelectFilterAuthorization

Ruby on-Rails Problem Overview


Let's say that I have a User model with attributes :id, :first_name, :last_name, and :email. In my application, guest users shouldn't see User's email and last_name. I know how to select specific values if I want a list of users but I don't know how I can get specific attributes of a specific user like

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

One solution to that is to user

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

but then I get a hash instead of an AR record. I could do

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

but the thing that I don't know which filters I should filter since I need to know the user first.

Could you help me?

Ruby on-Rails Solutions


Solution 1 - Ruby on-Rails

What Choco said will work if you want an array not an active record instance. If you want an active record instance, do:

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

Here's some more info. I'm not sure how much you do/don't know, so I'll assume you know less rather than more.

I assume you realise what you're doing above is method chaining - ie, you're calling a method, then calling another method on the result of the first method. For example:

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

You're calling the find_by_id method on the User class. That method will return an instance of the User class (ie, an active record instance). That instance has a method called attributes, which you call. The attributes method returns an instance of the Hash class, representing the fields in the previous active record instance. Hash has a slice method, which you call in turn. The slice method returns another instance of Hash, containing the subset of fields you specified.

So, the concept to be clear on is that when you're chaining methods, you're not calling each subsequent method on the same thing - you're calling it on the return value of the previous method in the chain.

OK, so with that out of the way:

All the find methods are intended to query the database and return a single instance of an Active Record model, or a plain ruby array containing multiple instances of Active Record models.

A lot of other methods - select, where, etc, do NOT hit the database right away, and are NOT intended to return an active record instance. They all return an instance of ActiveRecord::Relation. An ActiveRecord::Relation is kinda like a potential group of records matching certain conditions - and you can add extra conditions to it. It's like a 'database query waiting to happen', or a 'database query that may or may not have happened yet'.

When you call a method like where, order, or select on ActiveRecord::Relation, it will return an instance of ActiveRecord::Relation, meaning you've got an instance of the same class, and can chain methods from that class nicely eg: User.where(name: 'Fred').select(:id, :name).order('name ASC')

Your instance of ActiveRecord::Relation will be very smart, and won't bother hitting the database until you call a method that clearly indicates you want the records now - such as each, all, take, etc.

So, I've gone on way longer than planned here, so I'll wrap up. Here's the code I first mentioned, with a broken down, heavily commented explanation below:

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

breaking it down:

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... I went on longer than expected. Hope some of it was useful!

Solution 2 - Ruby on-Rails

Try this:

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

it returns array containing id and first_name values

Solution 3 - Ruby on-Rails

This will works as well

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

Solution 4 - Ruby on-Rails

Had a requirement like this but wanted something more generic that would just gather a list of method names and values from a model (including attributes). So, I added this method to my model:

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

Then, call this method with:

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

returns

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionvasilakisfilView Question on Stackoverflow
Solution 1 - Ruby on-Railsjoshua.palingView Answer on Stackoverflow
Solution 2 - Ruby on-RailsChocoView Answer on Stackoverflow
Solution 3 - Ruby on-RailstkhuynhView Answer on Stackoverflow
Solution 4 - Ruby on-RailsMike StateView Answer on Stackoverflow