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?
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!
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
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)
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