Obtener atributos específicos de un modelo ActiveRecord


Digamos que tengo un modelo User con atributos :id, :first_name, :last_name, and :email. En mi aplicación, los usuarios invitados no deberían ver User's email and last_name. Sé cómo seleccionar valores específicos si quiero una lista de usuarios, pero no se cómo puedo obtener atributos específicos de un usuario específico como

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

Una solución para eso es el usuario

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

Pero luego obtengo un hash en lugar de un registro AR. Podría hacer

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

Pero lo que no sé qué filtros debo filtrar ya que necesito conocer al usuario primero.

¿Podrías ayudarme?

Author: Choco, 2014-11-20

3 answers

Lo que dijo Choco funcionará si desea una matriz no una instancia de active record. Si desea una instancia de active record, haga lo siguiente:

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

Aquí hay más información. No estoy seguro de cuánto sabes o no sabes, así que asumiré que sabes menos en lugar de más.

Asumo que te das cuenta de lo que estás haciendo arriba es encadenamiento de métodos - es decir, estás llamando a un método, luego llamando a otro método en el resultado del primer método. Por ejemplo:

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

Estás llamando al método find_by_id en la clase User. Ese método devolverá una instancia de la clase User (es decir, una instancia de active record). Esa instancia tiene un método llamado attributes, que usted llama. El método attributes devuelve una instancia de la clase Hash, que representa los campos de la instancia anterior de active record. Hash tiene un método slice, al que llamas a su vez. El método slice devuelve otra instancia de Hash, que contiene el subconjunto de campos especificados.

Entonces, el concepto para ser claro es que cuando estás encadenando métodos, no estás llamando a cada método posterior en la misma cosa, lo estás llamando en el valor devuelto del método anterior en la cadena.

OK, así que con eso fuera del camino:

Todos los métodos find están destinados a consultar la base de datos y devolver una sola instancia de un modelo de Active Record, o una matriz ruby simple que contiene varias instancias de modelos de Active Record.

Muchos otros métodos - select, where, etc, no golpee el base de datos de inmediato, y NO están destinados a devolver una instancia de active record. Todos ellos devuelven una instancia de ActiveRecord::Relation. Un ActiveRecord::Relation es un poco como un grupo potencial de registros que coinciden con ciertas condiciones - y puede agregar condiciones adicionales a él. Es como una 'consulta de base de datos esperando a suceder', o una 'consulta de base de datos que puede o no haber sucedido todavía'.

Cuando llamas a un método como where, order, o select en ActiveRecord::Relation, devolverá una instancia de ActiveRecord::Relation, lo que significa que tienes una instancia de la misma clase, y puede encadenar métodos de esa clase muy bien por ejemplo: User.where(name: 'Fred').select(:id, :name).order('name ASC')

Su instancia de ActiveRecord::Relation será muy inteligente, y no se molestará en golpear la base de datos hasta que llame a un método que indique claramente que desea los registros ahora , como each, all, take, etc.

Por lo tanto, he ido mucho más de lo planeado aquí, así que voy a terminar. Aquí está el código que mencioné por primera vez, con una explicación detallada y muy comentada a continuación:

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

Romperlo abajo:

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... Fui más de lo esperado. Espero que algo de esto haya sido útil!

 74
Author: joshua.paling,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-11-20 11:29:33

Prueba esto:

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

Devuelve una matriz que contiene los valores id y first_name

 17
Author: Choco,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-11-20 10:48:10

Esto también funciona

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

 3
Author: tkhuynh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-07-07 16:53:39