Construir vs nuevo en Rails 3

125

En los documentos de Rails 3 , el buildmétodo para las asociaciones se describe como el mismo que el newmétodo, pero con la asignación automática de la clave externa. Directamente de los documentos:

Firm#clients.build (similar to Client.new("firm_id" => id))

He leído similares en otros lugares.

Sin embargo, cuando uso new(por ejemplo, some_firm.clients.newsin ningún parámetro), la firm_idasociación del nuevo cliente se crea automáticamente. ¡Estoy mirando los resultados ahora mismo en la consola!

¿Me estoy perdiendo de algo? ¿Los documentos están un poco desactualizados (poco probable)? ¿Cuál es la diferencia entre buildy new?

Cierre Vaquero
fuente
3
Las personas que buscan una respuesta rápida, marque la segunda respuesta: "construir" es solo un alias para "nuevo"
ivanreese

Respuestas:

208

Estás leyendo mal los documentos un poco. some_firm.client.newestá creando un nuevo Clientobjeto de la colección de clientes y, por lo tanto, puede establecerlo automáticamente firm_iden some_firm.id, mientras que los documentos están llamando Client.newy no tienen ningún conocimiento de la identificación de ninguna empresa, por lo que necesita firm_idpasarla.

La única diferencia entre some_firm.clients.newy some_firm.clients.buildparece ser que buildtambién agrega el cliente recién creado a la clientscolección:

henrym:~/testapp$ rails c
Loading development environment (Rails 3.0.4)
r:001 > (some_firm = Firm.new).save # Create and save a new Firm
#=> true 
r:002 > some_firm.clients           # No clients yet
#=> [] 
r:003 > some_firm.clients.new       # Create a new client
#=> #<Client id: nil, firm_id: 1, created_at: nil, updated_at: nil> 
r:004 > some_firm.clients           # Still no clients
#=> [] 
r:005 > some_firm.clients.build     # Create a new client with build
#=> #<Client id: nil, firm_id: 1, created_at: nil, updated_at: nil> 
r:006 > some_firm.clients           # New client is added to clients 
#=> [#<Client id: nil, firm_id: 1, created_at: nil, updated_at: nil>] 
r:007 > some_firm.save
#=> true 
r:008 > some_firm.clients           # Saving firm also saves the attached client
#=> [#<Client id: 1, firm_id: 1, created_at: "2011-02-11 00:18:47",
updated_at: "2011-02-11 00:18:47">] 

Si está creando un objeto a través de una asociación, builddebe preferirse a newque build mantenga su objeto en memoria some_firm(en este caso) en un estado coherente incluso antes de que los objetos se hayan guardado en la base de datos.

henrym
fuente
8
Utilizando some_firm.client.newtambién añade que el cliente some_firm.clients, y llamando saveen some_firmdio lugar a un error de validación que indica que clientno era válido. Si ambos newy buildagregan el nuevo cliente a some_firmla colección de clientes, ¿qué hace buildeso que newno hace? Lo siento por ser denso, aquí!
ClosureCowboy
1
+1 Recibí tu resultado con 3.0.4. Me encantaría que alguien con 3.0.3 pudiera confirmar que no estoy loco.
ClosureCowboy
41
@henrym Parece que en 3.2.6 clients.new y clients.build son similares en que ambos agregan el nuevo objeto a la colección. Quería agregar un comentario para cualquiera que se encontrara con esto mientras buscaba en Google como lo hice
hubbard
11
Parece que no hay diferencia entre ellos en Rails 3.2.3
Aditya Kapoor
44
Esta respuesta no es correcta para Rails> 3.2.13, donde 'build' es solo un alias para 'new'. Vea la respuesta de @ HatemMahmoud a continuación.
Andreas
91

buildes solo un alias para new:

alias build new

Se puede encontrar el código completo: https://github.com/rails/rails/blob/master/activerecord/lib/active_record/relation.rb#L74

Hatem Mahmoud
fuente
13
alias build newa partir de rieles 3.2.13
fontno
77
Esto solo es cierto para algunas asociaciones / relaciones. Las asociaciones singulares, por ejemplo, tienen definiciones completamente diferentes para buildy build_#{association}. Mira aquí y aquí .
coreyward
1
¿Esto sigue siendo cierto para Rails 4?
fatman13
1
aquí está el informe de error ... que sugiere si estaba usando nuevos como restaurant.customers.new, como una forma de asociar a un nuevo cliente a restaurant sin agregarlo a restaurant.customers, para usar scoped ... como restaurant .customers.scoped.new
user3334690
11

Tiene razón, la compilación y las nuevas funciones tienen el mismo efecto de establecer la clave foránea, cuando se llaman a través de una asociación. Creo que la razón por la cual la documentación está escrita de esta manera es para aclarar que se está instanciando un nuevo objeto Cliente, en lugar de una nueva relación de registro activa. Este es el mismo efecto que tendría llamar a .new en una clase en Ruby. Es decir que la documentación aclara que llamar a construir en una asociación es lo mismo que crear un nuevo objeto (llamar a .new) y pasar las claves foráneas a ese objeto. Estos comandos son todos equivalentes:

Firm.first.clients.build
Firm.first.clients.new
Client.new(:firm_id => Firm.first.id)

Creo que la razón por la que existe .build es que Firm.first.clients.new podría interpretarse en el sentido de que está creando un nuevo objeto de relación has_many, en lugar de un cliente real, por lo que llamar a .build es una forma de aclarar esto.

Pan Thomakos
fuente
Entonces son equivalentes. Eso es definitivamente lo que parece. ¡Gracias!
ClosureCowboy
55
Esto no es correcto. Los dos primeros son equivalentes en versiones posteriores de Rails (parece que en el momento de la publicación no lo eran). PERO, el último tiene una diferencia significativa en que los firmes.primeros.clientes no contendrán al nuevo cliente.
tybro0103
4

buildvs new:

en su mayoría nuevos y compilados son iguales, pero compilar almacena objetos en la memoria ,

p.ej:

para nuevo:

Client.new(:firm_id=>Firm.first.id)

Para construir:

Firm.first.clients.build

Aquí los clientes se almacenan en la memoria, cuando se guardan firmes, los registros asociados también se guardan.

Sarwan Kumar
fuente
2

Modelo nuevo

Tag.new post_id: 1instanciará una etiqueta con su post_idconjunto.

@ model.models.new

@post.tags.buildhace lo mismo Y la etiqueta instanciada estará @post.tagsincluso antes de que se guarde.

Esto significa @post.saveque guardará tanto la etiqueta @post como la recién creada (suponiendo que se establezca inverse_of). Esto es genial porque Rails validará ambos objetos antes de guardarlos, y ninguno se guardará si alguno de ellos falla la validación.

models.new vs models.build

@post.tags.buildy @post.tags.newson equivalentes (al menos desde Rails 3.2).

tybro0103
fuente
que tal esto The only difference between some_firm.clients.new and some_firm.clients.build seems to be that build also adds the newly-created client to the clients collection:?
ア レ ッ ク ス