2013-05-20 19 views
8

I due modelli User e Submission come segue:first_or_create per e-mail e quindi salvare il modello nidificato

class User < ActiveRecord::Base 
    # Associations 
    has_many :submissions 
    accepts_nested_attributes_for :submissions 

    # Setup accessible (or protected) attributes for your model 
    attr_accessible :email, :name, :role, :submission_ids, :quotation_ids, :submissions_attributes 

    validates :email, :presence => {:message => "Please enter a valid email address" } 
    validates :email, :uniqueness => { :case_sensitive => false } 
end 

class Submission < ActiveRecord::Base 
    belongs_to :user 
    attr_accessible :due_date, :text, :title, :word_count, :work_type, :rush, :user, :notes 

    validates :work_type, :title, :text,:presence => true 
    validates :text, :length => { :minimum => 250 } 
    validates :word_count, :numericality => { :only_integer => true } 
end 

Ho una forma che raccoglie i dati richiesti da questi due modelli. Controller utenti:

def index 
    @user = User.new 
    @user.submissions.build 
end 

def create 
    @user = User.where(:email => params[:user][:email]).first_or_create(params[:user]) 

    if @user 
    redirect_to :root 
    else 
    render 'pages/index' 
    end 
end 

Quello che voglio fare è verificare innanzitutto se l'utente esiste già nel sistema tramite l'e-mail inviata. Se è così allora voglio creare una sottomissione per quell'utente. In caso contrario, crea l'utente e l'invio contemporaneamente.

Sono confuso su come farlo con il metodo first_or_create.

Qualsiasi aiuto apprezzato.

risposta

13

first_or_createaccepts a block. Così si potrebbe fare come segue:

@user = User.where(:email => params[:user][:email]).first_or_create do |user| 
    # This block is called with a new user object with only :email set 
    # Customize this object to your will 
    user.attributes = params[:user] 
    # After this, first_or_create will call user.create, so you don't have to 
end 
+0

Dovrei "unire" invece di assegnarlo, ma questa è la mia preferenza personale. Pulisci un po '. –

+0

Grazie sembra essere la risposta più pulita – chell

+0

create viene chiamato solo se l'utente non esiste già nel database. Ho trovato che questo creava un problema perché non creava l'oggetto di sottomissione quando l'utente esiste già – chell

-3

Hey Penso che dovrebbe essere qualcosa di simile

@user = User.first_or_create(params[:user]) 

Poi, nel modello di utente

def first_or_create(params) 
    unless user = User.where(:email => params[:email]).first # try to find user 
    user = User.create(email: params[:user]) 
    # it should create also submission because of accepts_nested_attributes_for 
    else #user exsists so we need to create submission for him 
    user.submissions.create(params[:submissions]) 
    end 
end 
+1

[ 'first_or_create'] (http://api.rubyonrails.org/classes/ActiveRecord/Relation.html#method-i-first_or_create) esiste in ActiveRecord già. Non dovresti scrivere il tuo. – Mischa

+0

Se l'utente non esiste, questo codice non crea gli invii. – davogones

1

Dal momento che il caso d'uso è un po 'più complicato, non può ferire per dividere questo in su in due azioni separate. Se vuoi che questo avvenga atomicamente, puoi lanciarlo in un transaction.

User.transaction do 
    # Create the user if they don't already exist 
    @user = User.where(:email => params[:user][:email]).first_or_create 

    # Update with more attributes, and create nested submissions 
    @user.update_attributes(params[:user]) 
end 
+0

Perché dovrei fare questo come una transazione? Questo creerà anche l'oggetto di sottomissione? – chell

+0

Sto assumendo che i params assomiglino a '{user: ..., submissions_attributes: {...}}', dato che nel tuo modello hai 'accept_nested_attributes_for: submissions_attributes'. Il modello correlato verrà creato automaticamente se si nidificano gli attributi di invio. La transazione assicurerà che tutto venga creato/aggiornato in modo atomico (vale a dire che tutto ha successo o tutti i rollback). – davogones

+0

Mi piace anche questa risposta. Sono un NOOB e non sono sicuro quale sia la migliore risposta. Sono andato con quello di RDX perché usava solo un metodo. – chell

Problemi correlati