2013-05-20 11 views
8

I dwa modele User i Submission następująco:first_or_create przez e-mail, a następnie zapisać zagnieżdżonego modelowi

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 

Mam formularz, który zbiera dane wymagane przez tych dwóch modeli. Kontroler użytkowników:

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 

Co chcę zrobić, to najpierw sprawdzić, czy użytkownik już istnieje w systemie przez przesłany e-mail. Jeśli tak, chcę utworzyć zgłoszenie dla tego użytkownika. W przeciwnym razie utwórz użytkownika i przesyłaj w tym samym czasie.

Jestem zdezorientowany, jak to zrobić z metodą first_or_create.

Każda pomoc doceniona.

Odpowiedz

13

first_or_createaccepts a block. Więc można zrobić to w następujący sposób:

@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

Powinienem "scalić" zamiast przypisywać to, ale to jest moja osobista preferencja. Czystsze trochę. –

+0

Dzięki, że wydaje się być najczystszym odpowiedzi – chell

+0

tworzenie jest wywoływana tylko wtedy, gdy użytkownik nie istnieje już w bazie danych. Stwierdziłem, że to stworzyło problem, ponieważ nie utworzyłoby obiektu do przesłania, gdy użytkownik już istnieje. – chell

-3

Hej Myślę, że powinno być coś takiego

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

Następnie w modelu użytkownika

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) już istnieje w ActiveRecord. Nie powinieneś pisać własnego. – Mischa

+0

Jeśli użytkownik nie istnieje, ten kod nie tworzy zgłoszeń. – davogones

1

Ponieważ przypadek użycia jest trochę bardziej skomplikowana, może nie zaszkodzi podzielić to aż do dwa oddzielne działania. Jeśli chcesz, aby wystąpiło to atomowo, możesz rzucić je w 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

Dlaczego miałbym to zrobić jako transakcję? Czy to również utworzy obiekt przesyłania? – chell

+0

Domyślam się, że twoje parametry wyglądają jak '{user: ..., submissions_attributes: {...}}', ponieważ miałeś 'accepts_nested_attributes_for: submissions_attributes' w swoim modelu. Odpowiedni model zostanie utworzony automagicznie, jeśli zagnieżdżymy atrybuty przesyłania. Transakcja zapewni, że wszystko zostanie stworzone/zaktualizowane atomowo (tzn. Albo wszystko się powiedzie, albo wszystkie wycofa się). – davogones

+0

Podoba mi się również ta odpowiedź. Jestem NOOB i nie wiem, która jest najlepsza odpowiedź. Poszedłem z tym z RDX, ponieważ użyło tylko jednej metody. – chell

Powiązane problemy