ruby on rails - multiple - Rails find_or_create_by más de un atributo?
find_or_create_by rails 5 (5)
Hay un atributo dinámico útil en el registro activo llamado find_or_create_by:
Model.find_or_create_by_<attribute>(:<attribute> => "")
Pero, ¿qué sucede si necesito encontrar o crear más de un atributo?
Digamos que tengo un modelo para manejar una relación M: M entre el Grupo y el Miembro llamado GroupMember. Podría tener muchas instancias en las que member_id = 4, pero no quiero más de una vez que instancias donde member_id = 4 y group_id = 7. Estoy tratando de averiguar si es posible hacer algo como esto:
GroupMember.find_or_create(:member_id => 4, :group_id => 7)
Me doy cuenta de que puede haber mejores maneras de manejar esto, pero me gusta la conveniencia de la idea de find_or_create.
Al pasar un bloque a find_or_create
, puede pasar parámetros adicionales que se agregarán al objeto si se crea nuevo. Esto es útil si está validando la presencia de un campo que no está buscando.
Asumiendo:
class GroupMember < ActiveRecord::Base
validates_presence_of :name
end
entonces
GroupMember.where(:member_id => 4, :group_id => 7).first_or_create { |gm| gm.name = "John Doe" }
creará un nuevo GroupMember con el nombre "John Doe" si no encuentra uno con member_id 4
and group_id 7
En Rails 4 podías hacer:
GroupMember.find_or_create_by(member_id: 4, group_id: 7)
Y usar where
sea diferente:
GroupMember.where(member_id: 4, group_id: 7).first_or_create
Esto llamará a create
en GroupMember.where(member_id: 4, group_id: 7)
:
GroupMember.where(member_id: 4, group_id: 7).create
Por el contrario, find_or_create_by(member_id: 4, group_id: 7)
llamará a create
en GroupMember
:
GroupMember.create(member_id: 4, group_id: 7)
Por favor vea este commit relevante sobre rieles / rieles.
Para cualquier otra persona que se tropiece con este hilo pero necesita encontrar o crear un objeto con atributos que puedan cambiar dependiendo de las circunstancias, agregue el siguiente método a su modelo:
# Return the first object which matches the attributes hash
# - or -
# Create new object with the given attributes
#
def self.find_or_create(attributes)
Model.where(attributes).first || Model.create(attributes)
end
Sugerencia de optimización: independientemente de la solución que elija, considere agregar índices para los atributos que consulta con más frecuencia.
Se pueden conectar múltiples atributos con un and
:
GroupMember.find_or_create_by_member_id_and_group_id(4, 7)
(use find_or_initialize_by
si no desea guardar el registro de inmediato)
Edición: el método anterior está en desuso en Rails 4. La nueva forma de hacerlo será:
GroupMember.where(:member_id => 4, :group_id => 7).first_or_create
y
GroupMember.where(:member_id => 4, :group_id => 7).first_or_initialize
Edición 2: No todos estos factores fueron factorizados fuera de los carriles, solo los atributos específicos.
https://github.com/rails/rails/blob/4-2-stable/guides/source/active_record_querying.md
Ejemplo
GroupMember.find_or_create_by_member_id_and_group_id(4, 7)
convirtió
GroupMember.find_or_create_by(member_id: 4, group_id: 7)
Tu puedes hacer:
User.find_or_create_by(first_name: ''Penélope'', last_name: ''Lopez'')
User.where(first_name: ''Penélope'', last_name: ''Lopez'').first_or_create
O simplemente inicializar:
User.find_or_initialize_by(first_name: ''Penélope'', last_name: ''Lopez'')
User.where(first_name: ''Penélope'', last_name: ''Lopez'').first_or_initialize