Ruby on Rails - Not saving User - Nested Attribute

2019-09-12 12:37发布

问题:

I have this model User, Entidade and Candidato.

class User < ActiveRecord::Base
  has_one :entidade
  has_one :candidato

  accepts_nested_attributes_for :entidade
  accepts_nested_attributes_for :candidato

class Candidato < ActiveRecord::Base
  belongs_to :user

class Entidade < ActiveRecord::Base
  belongs_to :user

Basically in order to register you need to specify if you want to be an Entidade or a Candidato. They have some shared attributes that i put in the User table. And the non shared attributes stay in the respective table.

This is the form:

<%= simple_form_for @user, :html => { :multipart => true } do |f| %>
          <%= render 'shared/error_messages' %>
          <%= f.input :email %>
          <%= f.input :role, :as => :hidden, :input_html => { :value => "candidato" } %>
          <%= f.input :password %>
          <%= f.input :password_confirmation, label: "Confirme a password" %>
          <%= f.input :nome %>
          <%= f.input :foto, :label => "Foto" %>
          <%= f.input :cod_postal, :label => "Código-Postal" %>
          <%= f.input :localidade %>
          <%= f.input :contacto1, :label => "Contactos" %>
          <%= f.input :contacto2, label: false %>
          <%= f.input :pagina, :label => "Página Pessoal" %>
          <%= f.fields_for :candidato do |ff| %>
            <%= ff.input :data_nascimento, :label => "Data de Nascimento" %>
            <%= ff.input :bi, :label => "Bilhete de Identidade" %>
            <%= ff.input :cv, :label => "Curriculum Vitae" %>
            <%= ff.label :area_profissional, :label => "Área Profissional" %>
            <%= ff.select :area_profissional, ["Programador_Web", "Programador_Java","Gestor"], :label => "Área Profissional"  %>
            <%= ff.input :apresentacao, :label => "Apresentação" %>
            <%= ff.select :nivel_hab, ["Secundário","Licenciatura","Mestrado","Doutoramento"], :label => "Nível de Habilitações" %>
            <%= ff.input :hab_literaria, :label => "Habilitações Literárias" %>
            <%= ff.select :situacao_prof, ["Empregado","Desempregado"], :label => "Situação Profissional" %>
            <%= ff.input :exp_profissional, :label => "Experiência Profissional" %>
          <% end %>
          <%= f.submit "Registar", class: "btn btn-large btn-primary" %>
        <% end %>

And I can't create the damn User. It keeps rendering the new page. What the hell is wrong.

This is my Controller:

class UsersController < ApplicationController
  def new
    @user = User.new
    if params[:param] == "candidato"
      @role = "candidato"
      #@user.candidato = Candidato.new
      @user.build_candidato
    else
      @role = "entidade"
      #@user.entidade = Entidade.new
      @user.build_entidade
    end
  end

  def create
    @user = User.new(user_params)
    if user_params[:role] == "candidato"
      @user.build_candidato(user_params[:candidato_attributes])
      #@user.candidato = Candidato.new(user_params[:candidato_attributes])
      if @user.save
        #Sucesso
        redirect_to root_path
      else
        #Falhou
        @role = "candidato"
        render 'new'
      end
    else
      #@user.entidade = Entidade.new(user_params[:entidade_attributes])
      @user.build_entidade(user_params[:entidade_attributes])
      if @user.save
        #Sucesso
        redirect_to root_path
      else
        #Falhou
        @role = "entidade"
        render 'new'
      end
    end
  end

  private
    def user_params
      params.require(:user).permit(:role,:email,:nome,:password,:password_confirmation,:pagina,:contacto1,:contacto2,:foto,:cod_postal,:localidade, :candidato_attributes => [:data_nascimento,:bi,:cv,:area_profissional,:apresentacao,:nivel_hab,:hab_literaria,:situacao_prof,:exp_profissional], :entidade_attributes => [:nip,:apresentacao,:atividade])
    end
end

If someone knows what's wrong please tell me

回答1:

Problem is here in your create method you are building dependent object twice and you have has_one relationship. You object for dependent model already created on new action on parent.

Your controller should look like :

def create
  @user = User.new(user_params)
  if @user.save
    redirect_to root_path
  else
    @role = user_params[:role]
    render 'new'
  end
end

Form should look like :

<%= simple_form_for @user, :html => { :multipart => true } do |f| %>
  <%= render 'shared/error_messages' %>
  <%= f.input :email %>
  <%= f.input :role, :as => :hidden, :input_html => { :value => @role } %>
  <%= f.input :password %>
  <%= f.input :password_confirmation, label: "Confirme a password" %>
  <%= f.input :nome %>
  <%= f.input :foto, :label => "Foto" %>
  <%= f.input :cod_postal, :label => "Código-Postal" %>
  <%= f.input :localidade %>
  <%= f.input :contacto1, :label => "Contactos" %>
  <%= f.input :contacto2, label: false %>
  <%= f.input :pagina, :label => "Página Pessoal" %>
  <% if @role == "candidato" %>
    <%= f.fields_for :candidato do |ff| %>
      <%= ff.input :data_nascimento, :label => "Data de Nascimento" %>
      <%= ff.input :bi, :label => "Bilhete de Identidade" %>
      <%= ff.input :cv, :label => "Curriculum Vitae" %>
      <%= ff.label :area_profissional, :label => "Área Profissional" %>
      <%= ff.select :area_profissional, ["Programador_Web", "Programador_Java","Gestor"], :label => "Área Profissional"  %>
      <%= ff.input :apresentacao, :label => "Apresentação" %>
      <%= ff.select :nivel_hab, ["Secundário","Licenciatura","Mestrado","Doutoramento"], :label => "Nível de Habilitações" %>
      <%= ff.input :hab_literaria, :label => "Habilitações Literárias" %>
      <%= ff.select :situacao_prof, ["Empregado","Desempregado"], :label => "Situação Profissional" %>
      <%= ff.input :exp_profissional, :label => "Experiência Profissional" %>
    <% end %>
  <%else%>
    <%= f.fields_for :entidade do |ff| %>
      <%= ff.input :atividade, :label => "atividade" %>
      <%= ff.input :apresentacao, :label => "apresentacao" %>
      <%= ff.input :nip, :label => "nip" %>
    <% end %>
  <% end %>
  <%= f.submit "Registar", class: "btn btn-large btn-primary" %>
<% end %>

You also have to add :id and _destroy in attributes. It will used at the time of edit and delete child model.

def user_params
  params.require(:user).permit(:role,:email,:nome,:password,:password_confirmation,:pagina,:contacto1,:contacto2,:foto,:cod_postal,:localidade, :candidato_attributes => [:id, :data_nascimento,:bi,:cv,:area_profissional,:apresentacao,:nivel_hab,:hab_literaria,:situacao_prof,:exp_profissional, :_destroy], :entidade_attributes => [:id, :nip,:apresentacao,:atividade, :_destroy])
end


回答2:

#app/controllers/users_controller.rb
Class UsersController < ApplicationController
   def new
      @user = User.new
      @user.send("build_#{params[:param]}")
   end

   def create
      @user = User.new user_params
      @user.save
   end

   private

   def user_params
      params.require(:user).permit(:role,:email,:nome,:password,:password_confirmation,:pagina,:contacto1,:contacto2,:foto,:cod_postal,:localidade, :candidato_attributes => [:data_nascimento,:bi,:cv,:area_profissional,:apresentacao,:nivel_hab,:hab_literaria,:situacao_prof,:exp_profissional], :entidade_attributes => [:nip,:apresentacao,:atividade])
   end
end

When you pass nested attributes, you only need to build the initial associative object

In your create method, you're building the associative data again. A much better way will be to use the code above (albeit edited to represent your redirects), to create the User object

Validations aside, I don't see any reason why the above code wouldn't work with your form