Construire vs nouveau dans Rails 3

125

Dans les rails 3 docs , la buildméthode des associations est décrit comme étant la même que la newméthode, mais avec l'affectation automatique de la clé étrangère. Directement depuis la documentation:

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

J'ai lu la même chose ailleurs.

Cependant, lorsque j'utilise new(par exemple some_firm.clients.newsans aucun paramètre), l' firm_idassociation du nouveau client est automatiquement créée. Je regarde les résultats maintenant dans la console!

Est-ce que je manque quelque chose? Les documents sont-ils un peu dépassés (peu probable)? Quelle est la différence entre buildet new?

FermetureCowboy
la source
3
Les gens qui recherchent une réponse rapide, vérifiez la deuxième vers le bas: "build" est juste un alias pour "new"
ivanreese

Réponses:

208

Vous lisez légèrement la documentation. some_firm.client.newcrée un nouvel Clientobjet à partir de la collection clients, et il peut donc automatiquement définir le firm_idsur some_firm.id, alors que les documents appellent Client.newqui n'ont aucune connaissance de l'identifiant de l'entreprise, il doit donc lui être firm_idtransmis.

La seule différence entre some_firm.clients.newet some_firm.clients.buildsemble être que buildajoute également le client nouvellement créé à la clientscollection:

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 vous créez un objet via une association, builddoit être préféré ànew la construction, car la construction conserve votre objet en mémoire some_firm(dans ce cas) dans un état cohérent avant même que les objets aient été enregistrés dans la base de données.

Henrym
la source
8
L'utilisation some_firm.client.newajoute également le client à some_firm.clients, et l'appel savesur some_firmentraîne une erreur de validation indiquant qu'il clientn'est pas valide. Si les deux newet buildajoutez le nouveau client à some_firmla collection de clients de, qu'est- buildce que newcela ne fait pas? Je suis désolé d'être dense, ici!
ClosureCowboy
1
+1 J'ai reçu votre résultat avec 3.0.4. J'adorerais si quelqu'un avec 3.0.3 pouvait confirmer que je ne suis pas fou.
ClosureCowboy
41
@henrym Il semble que dans la version 3.2.6, clients.new et clients.build sont similaires en ce qu'ils ajoutent tous les deux le nouvel objet dans la collection. Je voulais ajouter un commentaire pour tous ceux qui sont
tombés
11
On dirait qu'il n'y a pas de différence entre eux dans Rails 3.2.3
Aditya Kapoor
4
Cette réponse n'est pas correcte pour Rails> 3.2.13, où 'build' est juste un alias pour 'new'. Voir la réponse de @ HatemMahmoud ci-dessous.
Andreas
91

buildest juste un alias pour new:

alias build new

Le code complet peut être trouvé: https://github.com/rails/rails/blob/master/activerecord/lib/active_record/relation.rb#L74

Hatem Mahmoud
la source
13
alias build newà partir de rails 3.2.13
fontno
7
Ceci n'est vrai que pour certaines associations / relations. Les associations singulières, par exemple, ont des définitions entièrement différentes pour buildet build_#{association}. Voir ici et ici .
coreyward
1
Est-ce toujours vrai pour Rails 4?
fatman13
1
voici le rapport de bogue ... qui suggère si vous utilisiez new comme restaurant.customers.new, comme moyen d'obtenir un nouveau client associé au restaurant sans l'ajouter à restaurant.customers, à utiliser scoped ... comme restaurant .customers.scoped.new
user3334690
11

Vous avez raison, les fonctions build et new ont le même effet de définir la clé étrangère, lorsqu'elles sont appelées via une association. Je pense que la raison pour laquelle la documentation est écrite comme ceci est de clarifier qu'un nouvel objet Client est en cours d'instanciation, par opposition à une nouvelle relation d'enregistrement active. C'est le même effet que l'appel de .new sur une classe aurait dans Ruby. C'est-à-dire que la documentation précise que l'appel de build sur une association est la même chose que la création d'un nouvel objet (appelant .new) et la transmission des clés étrangères à cet objet. Ces commandes sont toutes équivalentes:

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

Je crois que la raison pour laquelle .build existe est que Firm.first.clients.new pourrait être interprété comme signifiant que vous créez un nouvel objet de relation has_many, plutôt qu'un client réel, donc appeler .build est un moyen de clarifier cela.

Pan Thomakos
la source
Donc, ils sont équivalents. C'est définitivement ce qu'il semble. Je vous remercie!
ClosureCowboy
5
Ce n'est pas correct. Les deux premiers sont équivalents dans les versions ultérieures de Rails (il semble qu'au moment de la publication, ils ne l'étaient pas). MAIS, le dernier a une différence significative en ce que Firm.first.clients ne contiendra pas le nouveau client.
tybro0103
4

buildvs new:

la plupart du temps new et build sont les mêmes mais build stocke l'objet en mémoire ,

par exemple:

pour les nouveaux:

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

Pour construire:

Firm.first.clients.build

Ici, les clients sont stockés en mémoire, lors de la sauvegarde de l'entreprise, les enregistrements associés sont également enregistrés.

Sarwan Kumar
la source
2

Model.new

Tag.new post_id: 1instanciera une balise avec son post_idensemble.

@ model.models.new

@post.tags.buildfait de même ET la balise instanciée se trouvera @post.tagsavant même qu'elle ne soit enregistrée.

Cela signifie @post.savesauvera à la fois le @post et la balise nouvellement construite (en supposant que inverse_of est défini). C'est génial car Rails validera les deux objets avant de les enregistrer, et aucun ne sera enregistré si l'un d'eux échoue à la validation.

models.new vs models.build

@post.tags.buildet @post.tags.newsont équivalents (au moins depuis Rails 3.2).

tybro0103
la source
et ça 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:?
ア レ ッ ク ス