2011-02-10 16 views
115

En los Rails 3 docs, el método build para asociaciones se describe como el mismo que el método new, pero con la asignación automática de la clave externa. Directamente de los documentos:Build vs new en Rails 3

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.new sin ningún parámetro), firm_id la asociación de la nueva cliente es creado automáticamente. ¡Estoy mirando los resultados ahora mismo en la consola!

¿Echo de menos algo? ¿Los documentos están un poco desactualizados (poco probable)? ¿Cuál es la diferencia entre build y new?

+3

Las personas que buscan una respuesta rápida, marque la segunda abajo: "compilar" es solo un alias para "nuevo" – ivanreese

Respuesta

199

Está mal leer los documentos un poco. some_firm.client.new está creando un nuevo objeto Client de la colección de clientes, por lo que puede establecer automáticamente firm_id en some_firm.id, mientras que los documentos están llamando al Client.new que no tiene ningún conocimiento de ninguna Firma, por lo que necesita el firm_id.

La única diferencia entre some_firm.clients.new y some_firm.clients.build parece ser que build añade también el cliente recién creado a la clients colecció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 va a crear un objeto a través de una asociación, debe preferirse build más de new ya que la compilación mantiene su objeto en memoria, some_firm (en este caso) en un estado constante incluso antes de que los objetos se hayan guardado en la base de datos.

+6

El uso de 'some_firm.client.new' también agrega al cliente a' some_firm.clients', y al llamar 'save' on' some_firm' se produce un error de validación que indica que 'client' no es válido. Si tanto 'new' como' build' agregan el nuevo cliente a la colección de clientes de 'some_firm', ¿qué hace' build' que 'new' no funcione? Lo siento por ser denso, aquí! – ClosureCowboy

+0

He agregado un ejemplo a mi publicación para aclarar más la diferencia entre 'build' y' new'. En cuanto al error de validación, supongo que su modelo de cliente necesita más datos antes de poder guardarlos (por ejemplo, un campo de nombre que no puede ser nulo). En ese caso, aún tendría que establecer los atributos antes de guardar. – henrym

+0

Gracias por esos detalles. Estoy mirando un comportamiento diferente en este momento, aunque todavía estoy en 3.0.3 (lo actualizaré y veré de nuevo en un momento). En 'r: 004',' clients' es una colección vacía; para mí, contiene el cliente 'nuevo'. Pan Thomakos describió el mismo comportamiento. Y el error de validación para el usuario dijo: '{: clients => [" is invalid "]}' – ClosureCowboy

11

Está en lo cierto, la compilación y las nuevas funciones tienen el mismo efecto de configurar la clave externa, cuando se llaman a través de una asociación. Creo que la razón por la cual la documentación está escrita así es para aclarar que se está creando una instancia de un nuevo objeto Cliente, a diferencia de una nueva relación de registro activo. Este es el mismo efecto que llamar .new en una clase tendría en Ruby. Es decir que la documentación está aclarando que invocar construir sobre una asociación es lo mismo que crear un nuevo objeto (llamar .new) y pasar las claves externas a ese objeto. Estos comandos son equivalentes:

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

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

+0

Entonces * son * equivalentes. Eso es definitivamente lo que parece. ¡Gracias! – ClosureCowboy

+5

Esto no es correcto. Los primeros dos 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 esa Firma. Los primeros clientes no contendrán el nuevo cliente. – tybro0103

85

build es sólo un alias para new:

alias build new 

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

+12

'alias build new' as of rails 3.2.13 – fontno

+7

Esto solo es cierto para algunas asociaciones/relaciones. Las asociaciones singulares, por ejemplo, tienen definiciones completamente diferentes para 'build' y' build _ # {association} '. Ver [aquí] (https://github.com/rails/rails/blob/master/activerecord/lib/active_record/associations/singular_association.rb#L28) y [aquí] (https://github.com/rails/ rails/blob/master/activerecord/lib/active_record/associations/builder/singular_association.rb # L17). – coreyward

+1

¿Sigue siendo cierto para 'Rails 4'? – fatman13

3

acumulación vs nueva:

sobre todo nuevo y de construcción son los mismos, pero construir objeto almacena en la memoria ,

por ejemplo.

de nuevo:

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

Para construir:

Firm.first.clients.build 

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

2

Model.new

Tag.new post_id: 1 creará una instancia de una etiqueta con su conjunto post_id.

@ model.models.new

@post.tags.build hace lo mismo Y la etiqueta de instancia será en @post.tags incluso antes de que se transforma en gol.

Esto significa @post.save guardará tanto la etiqueta @post como la etiqueta recién construida (suponiendo que se haya configurado "inverso_de"). 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.build y @post.tags.new son equivalentes (por lo menos desde Rails 3.2).

+0

¿qué tal esto 'La única diferencia entre some_firm .clients.new y some_firm.clients.build parece ser que build también agrega el cliente recién creado a la colección de clientes: '? –