ruby-on-rails - foreign - rails generate model
Guardar mĂșltiples objetos en una sola llamada en rieles (4)
Tengo un método en rails que está haciendo algo como esto:
a = Foo.new("bar")
a.save
b = Foo.new("baz")
b.save
...
x = Foo.new("123", :parent_id => a.id)
x.save
...
z = Foo.new("zxy", :parent_id => b.id)
z.save
El problema es que esto toma más y más tiempo cuanto más entidades agrego. Sospecho que esto es porque tiene que golpear la base de datos para cada registro. Como están anidados, sé que no puedo salvar a los niños antes de que se guarden los padres, pero me gustaría salvar a todos los padres a la vez, y luego a todos los niños. Sería bueno hacer algo como:
a = Foo.new("bar")
b = Foo.new("baz")
...
saveall(a,b,...)
x = Foo.new("123", :parent_id => a.id)
...
z = Foo.new("zxy", :parent_id => b.id)
saveall(x,...,z)
Eso lo haría todo en solo dos hits en la base de datos. ¿Hay alguna manera fácil de hacer esto en los rieles, o estoy atrapado haciéndolo de a uno por vez?
¡No necesitas una gema para golpear DB rápido y solo una vez!
Jackrg lo ha resuelto para nosotros: https://gist.github.com/jackrg/76ade1724bd816292e4e
Como necesita realizar varias inserciones, la base de datos se golpeará varias veces. El retraso en su caso se debe a que cada guardado se realiza en diferentes transacciones de DB. Puede reducir la latencia al encerrar todas sus operaciones en una transacción.
class Foo
belongs_to :parent, :class_name => "Foo"
has_many :children, :class_name => "Foo", :foreign_key=> "parent_id"
end
Su método de guardar podría verse así:
# build the parent and the children
a = Foo.new(:name => "bar")
a.children.build(:name => "123")
b = Foo.new("baz")
b.children.build(:name => "zxy")
#save parents and their children in one transaction
Foo.transaction do
a.save
b.save
end
La llamada de save
en el objeto principal guarda los objetos secundarios.
Puede intentar usar Foo.create en lugar de Foo.new. Crear "Crea un objeto (o varios objetos) y lo guarda en la base de datos, si se aprueban las validaciones. El objeto resultante se devuelve ya sea que el objeto se haya guardado correctamente en la base de datos o no".
Puede crear varios objetos como este:
# Create an Array of new objects
parents = Foo.create([{ :first_name => ''Jamie'' }, { :first_name => ''Jeremy'' }])
Luego, para cada padre, también puede usar crear para agregar a su asociación:
parents.each do |parent|
parent.children.create (:child_name => ''abc'')
end
Recomiendo leer tanto la documentación de ActiveRecord como las Guías de rieles en la interfaz de consulta de ActiveRecord y las asociaciones de ActiveRecord . Este último contiene una guía de todos los métodos que gana una clase cuando declaras una asociación.
Una de las dos respuestas encontradas en otro lugar: por Beerlington . Esos dos son su mejor apuesta para el rendimiento
Creo que su mejor apuesta en términos de rendimiento va a ser utilizar SQL, e insertar en bloque varias filas por consulta. Si puede compilar una instrucción INSERT que haga algo como:
INSERT INTO foos_bars (foo_id, bar_id) VALUES (1,1), (1,2), (1,3) .... Debería poder insertar miles de filas en una sola consulta. No probé tu método mass_habtm, pero parece que podrías hacer algo como:
bars = Bar.find_all_by_some_attribute(:a)
foo = Foo.create
values = bars.map {|bar| "(#{foo.id},#{bar.id})"}.join(",")
connection.execute("INSERT INTO foos_bars (foo_id, bar_id) VALUES
#{values}")
Además, si busca Bar por "some_attribute", asegúrese de tener ese campo indexado en su base de datos.
O
Aún puede echar un vistazo a activerecord-import. Es correcto que no funciona sin un modelo, pero podrías crear un modelo solo para la importación.
FooBar.import [:foo_id, :bar_id], [[1,2], [1,3]]
Aclamaciones