Search code examples
ruby-on-railsrubyassociationsnested-forms

Changing from has_many to has_one relation rails


Here personaldetails belongs_to user and the relation given is has_many which is wrong.I want to convert the has_many relation to has_one relation i.e. User has_one personaldetails. When I change the relation directly I am getting an error "uninitialized constant User::Personaldetails. Please guide me how to convert the relation .

Personaldetail.rb

class Personaldetail < ApplicationRecord
    belongs_to :user
end

User.rb

class User < ApplicationRecord
 has_many :personaldetails, dependent: :destroy
accepts_nested_attributes_for :personaldetails, reject_if: :all_blank, allow_destroy: true
end

routes.rb

 resources :users, except: [:new] do
  resources :personaldetails
 end

user_steps_controller.rb

class UserStepsController < ApplicationController
    include Wicked::Wizard
    steps : :personaldetails
    def show
        @user = current_user
        @personaldetails = @user.personaldetails.build
        render_wizard
    end

def update
    @user = current_user
    @user.update!(user_params)
    render_wizard @user
end
   
private

def user_params
    params.require(:user).permit(:name, :password, :password_confirmation, :user_id,
      personaldetails_attributes: [:id,:first_name, :last_name, :gmail, :mobile_no, :city, :state, :pin_code, :_destroy])
end
end

personaldetails.html.erb

<%= form_with(model: @user, url: wizard_path, local: true) do |form| %> 
  <%= form.fields_for :personaldetail,Personaldetail.new do |info| %>
    <%= render 'personaldetails_field', form: info %>
  <% end %>
  <%= form.submit %>
<% end %>

_personaldetails_field.html.erb

<div class="field">
<%= form.label :First_name %><br />
<%= form.text_field :first_name %>
</div>
<div class="field">
<%= form.label :Last_name %><br />
<%= form.text_field :last_name %>
</div>
<div class="field">
<%= form.label :email %><br />
<%= form.text_field :gmail %>
</div>
<div class="field">
<%= form.label :Mobile_number %><br />
<%= form.text_field :mobile_no %>
</div>
<div class="field">
<%= form.label :City %><br />
<%= form.text_field :city %>
</div>
<div class="field">
<%= form.label :State %><br />
<%= form.text_field :state %>
</div>
<div class="field">
<%= form.label :Pincode %><br />
<%= form.text_field :pin_code %>
</div>

So the solution is:

Personaldetail.rb

class Personaldetail < ApplicationRecord
    belongs_to :user
end

User.rb

class User < ApplicationRecord
 has_one :personaldetails, dependent: :destroy
accepts_nested_attributes_for :personaldetails, reject_if: :all_blank, allow_destroy: true
end

routes.rb

 resources :users, except: [:new] do
  resources :personaldetail
 end

user_steps_controller.rb

class UserStepsController < ApplicationController
    include Wicked::Wizard
    steps : :personaldetails
    def show
        @user = current_user
        render_wizard
    end

def update
    @user = current_user
    @user.update!(user_params)
    render_wizard @user
end
   
private

def user_params
    params.require(:user).permit(:name, :password, :password_confirmation, :user_id,
      personaldetails_attributes: [:id,:first_name, :last_name, :gmail, :mobile_no, :city, :state, :pin_code, :_destroy])
end
end

personaldetail.html.erb

<%= form_with(model: @user, url: wizard_path, local: true) do |form| %> 
  <%= form.fields_for :personaldetail,@user.personaldetail || @user.build_personaldetail do |info| %>
    <%= render 'personaldetail_field', form: info %>
  <% end %>
  <%= form.submit %>
<% end %>

_personaldetail_field.html.erb

<div class="field">
<%= form.label :First_name %><br />
<%= form.text_field :first_name %>
</div>
<div class="field">
<%= form.label :Last_name %><br />
<%= form.text_field :last_name %>
</div>
<div class="field">
<%= form.label :email %><br />
<%= form.text_field :gmail %>
</div>
<div class="field">
<%= form.label :Mobile_number %><br />
<%= form.text_field :mobile_no %>
</div>
<div class="field">
<%= form.label :City %><br />
<%= form.text_field :city %>
</div>
<div class="field">
<%= form.label :State %><br />
<%= form.text_field :state %>
</div>
<div class="field">
<%= form.label :Pincode %><br />
<%= form.text_field :pin_code %>
</div>

Solution

  • As in the comment by spickermann, has_many relationship wants plural form and has_one the singular form. That is to say, you should already be able to infer the relationship from:

    @user.personaldetails # user has many personal details
    @user.personaldetail  # user has one personal detail
    
    

    Just a consideration: many weird cases arise when objects/models are not properly named. As a rule of thumb, you should use the most fitting and precise English noun for the object you need to name. That will help you hugely in cases like this. In normal English language, it is somehow strange to say "a user has a personal detail" but you would say of course "has personal details". Particularly when it comes to ActiveRecord associations, Rails syntax should be the nearest as possible to English language, to avoid later misunderstandings. I guess this confusion would not have arisen if instead of "PersonalDetail", the model was called "Account" or "Profile", for instance.