2011-01-06 12 views
26

Ho una dichiarazione di creazione per alcuni modelli, ma sta creando un record all'interno di una tabella di join indipendentemente dal fatto che il record esista già.In Rails, qual è il modo migliore per aggiornare un record o crearne uno nuovo se non esiste?

Ecco ciò che il mio codice è simile:

@user = User.find(current_user) 
@event = Event.find(params[:id]) 
for interest in @event.interests 
@user.choices.create(:interest => interest, :score => 4) 
end 

Il problema è che si crea record non importa cosa. Mi piacerebbe che creasse un record solo se nessun record esiste già; se esiste un record, vorrei che prenda l'attributo del record trovato e aggiunga o sottragga 1.

Mi sono guardato in giro per aver visto qualcosa chiamato find_or_create_by. Cosa fa questo quando trova un record? Vorrei prendere l'attuale attributo :score e aggiungere 1.

È possibile trovare o creare id? Non sono sicuro di quale attributo troverei, dal momento che il modello che sto osservando è un modello di join che ha solo chiavi esterne id e l'attributo punteggio.

ho cercato

@user.choices.find_or_create_by_user(:user => @user.id, :interest => interest, :score => 4) 

ma ottenuto

metodo non definito find_by_user

Cosa devo fare?

+1

È current_user già un'istanza del modello User? Se è così, non è necessario ritrovarlo e puoi semplicemente usare 'current_user' invece di' @user = ... '. –

risposta

23

Assumendo che il modello Choice ha una user_id (da associare a un utente) e un interest_id (da associare a un interesse), qualcosa di simile dovrebbe fare il trucco:

@user = User.find(current_user) 
@event = Event.find(params[:id]) 

@event.interests.each do |interest| 
    choice = @user.choices.find_or_initialize_by_interest_id(interest.id) do |c| 
    c.score = 0 # Or whatever you want the initial value to be - 1 
    end 

    choice.score += 1 
    choice.save! 
end 

Alcune note:

  1. Non è necessario includere la colonna user_id nello find_or_*_by_*, come già indicato a Rails per recuperare soloappartenente a @user.
  2. Sto usando find_or_initialize_by_*, che è essenzialmente lo stesso di find_or_create_by_*, con l'unica differenza chiave è che initialize in realtà non crea il record. Questo sarebbe simile a Model.new in contrasto con Model.create.
  3. Il blocco che imposta c.score = 0 viene eseguito solo se il record è non.
  4. choice.score += 1 aggiornerà il valore del punteggio per il record, indipendentemente dal fatto che esista o meno. Quindi, il punteggio predefinito c.score = 0 dovrebbe essere il valore iniziale meno uno.
  5. Infine, choice.save! aggiornerà il record (se già esistente) o creerà il record avviato (se non lo fosse).
+0

che collega questa domanda simile http://stackoverflow.com/questions/18747062/rails-create-or-update-magic – fatman13

+0

Questa risposta deve essere aggiornata, alcuni metodi non sono adatti per le guide 4 –

7

find_or_create_by_user_id suona meglio

+0

Qual è la differenza? – MartinElvar

+1

questo non aggiorna il record? Lo trova o lo crea. – baash05

59
my_class = ClassName.find_or_initialize_by_name(name) 

my_class.update_attributes({ 
    :street_address => self.street_address, 
    :city_name => self.city_name, 
    :zip_code => self.zip_code 
}) 
+0

Perché giù voto? –

+6

non so perché questo è stato downvoted. questo è stato utile per me - grazie per il contributo! – shedd

+0

L'ho trovato anch'io utile. Direi che il downvote è perché ha solo parzialmente risposto a ciò che l'OP stava chiedendo. –

0

Se si utilizzano i binari 4 Non credo che crei i metodi di ricerca come in passato, quindi find_or_create_by_user non è stato creato per te. Invece devi fare in questo modo:

@user = User.find(current_user) 
@event = Event.find(params[:id]) 
for interest in @event.interests 
@user.choices.find_or_create_by(:interest => interest) do |c| 
    c.score ||= 0 
    c.score += 1 
end 
end 
3

Inoltre Rails 3 si può fare:

@user.choices.where(:user => @user.id, :interest => interest, :score => 4).first_or_create 
+0

'first_or_create' è preferito in Rails 4 , nel frattempo 'find_or_intialize_by_xxx (attribute)' è deprecato a favore di 'find_or_intialize_by (xxx: attribute)' –

0

In Rails 4 È possibile utilizzare find_or_create_by per ottenere un oggetto (se non esiste, creerà), quindi utilizzare update per salvare o aggiornare il record, il metodo di aggiornamento continuerà a registrare se non esiste, altrimenti aggiorna il record.

Per esempio

@edu = current_user.member_edu_basics.find_or_create_by(params.require(:member).permit(:school)) 

if @edu.update(params.require(:member).permit(:school, :majoy, :started, :ended)) 
Problemi correlati