Rails: usare build con un'associazione has_one in rails


143

In questo esempio, creo un usercon no profile, quindi in seguito creo un profileper quell'utente. Ho provato a usare build con has_oneun'associazione ma è esploso. L'unico modo in cui vedo questo funzionamento è l'utilizzo has_many. La usersi suppone di avere solo al massimo un profile.

Ci sto provando. Io ho:

class User < ActiveRecord::Base
  has_one :profile
end

class Profile < ActiveRecord::Base
  belongs_to :user
end

Ma quando lo faccio:

user.build_profile 

Ottengo l'errore:

ActiveRecord::StatementInvalid: Mysql::Error: Unknown column 'profiles.user_id' in 'where clause': SELECT * FROM `profiles` WHERE (`profiles`.user_id = 4)  LIMIT 1

Esiste un modo in rails per avere 0 o 1 associazione?


cosa hai provato esattamente? potresti, per favore, pubblicare un po 'di codice?
Ju Nogueira,

Risposte:


359

La buildfirma del metodo è diversa per has_onee le has_manyassociazioni.

class User < ActiveRecord::Base
  has_one :profile
  has_many :messages
end

La sintassi di build per l' has_manyassociazione:

user.messages.build

La sintassi di build per l' has_oneassociazione:

user.build_profile  # this will work

user.profile.build  # this will throw error

Leggi la documentazionehas_one dell'associazione per maggiori dettagli.


28
La diversa sintassi di has_one mi sorprende sempre ... dannazione!
Galaxy

11
È divertente come la risposta più votata e accettata qui stia rispondendo a una domanda diversa da quella che l'OP ha posto.
Ajedi32,

Presumibilmente se l'utente apparteneva al profilo (nel senso che la tabella utente ha chiave_ID_estera nella sua tabella), anche la creazione del profilo per l'utente funzionerà come menzionato sopra vale a dire, ma per le nuove azioni solo user.build_profile per la modifica user.build_profile if user.profile.nil? e se si desidera creare il profilo durante la creazione dell'utente, scrivere accepts_nested_attributes_for :profilequesto in Modello utente. e nella forma in cui l'utente viene creato, scrivi <%= f.simple_fields_for :profile do |p| %>questo e continua.
zelo,

ma perché questo diverso comportamento è stato mantenuto per has_one o has_many? Ci sarebbe qualche motivo durante la progettazione, penso e mi aspetto.
curioso

@ Ajedi32 la risposta corrisponde al titolo della domanda ma non al corpo. Dato che questo ( build_<association>) è un comportamento abbastanza strano e inaspettato in Rails, ci sono molte più persone che cercano questa risposta rispetto alla risposta delle domande reali, se sai cosa intendo.
Max Williams,

19

Dai un'occhiata al messaggio di errore. Ti sta dicendo che non hai richiesto la colonna user_idnella tabella del profilo . L'impostazione delle relazioni nel modello è solo una parte della risposta.

È inoltre necessario creare una migrazione che aggiunge la user_idcolonna alla tabella del profilo. Rails si aspetta che sia presente e, in caso contrario, non è possibile accedere al profilo.

Per maggiori informazioni, dai un'occhiata a questo link:

Nozioni di base sull'associazione


1
Ho appena capito il mio problema. Il libro da cui sto imparando non ha spiegato molto bene la creazione di chiavi esterne. Ho creato una nuova migrazione che aggiunge una chiave esterna al mio modello. Grazie.
espinet

Devi creare tu stesso la colonna ogni volta? Ho avuto l'idea che fosse successo automagicamente. Non so dove ho avuto quell'idea.
Rimian,

Puoi aggiungere la colonna quando generi un modello usando la riga di comando, qualcosa del genere rails g model profile user:references:index address:string bio:text.
duykhoa,

1

A seconda del caso d'uso, può essere conveniente avvolgere il metodo e creare automaticamente l'associazione quando non viene trovata.

old_profile = instance_method(:profile)
define_method(:profile) do
  old_profile.bind(self).call || build_profile
end

ora chiamando il #profilemetodo restituirà il profilo associato o costruirà una nuova istanza.

fonte: quando la scimmia corregge un metodo, puoi chiamare il metodo ignorato dalla nuova implementazione?


1
a rotaie attuali (testato su 6.0.2.2) è possibile semplificare questo: def profile; super || build_profile; end.
glasz,

-14

Dovrebbe essere un has_one. Se buildnon funziona, puoi semplicemente usare new:

ModelName.new( :owner => @owner )

equivale a

@owner.model_names.build

11
Questo non è lo stesso: se crei un nuovo nome_modello con build, quando viene salvato @owner, verrà salvato anche il nuovo nome_modello. Quindi, puoi usare build per creare un genitore e dei figli che verranno salvati insieme. Questo non è il caso se si crea un nome_modello con .new
Max Williams
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.