Vishal
Vishal

Reputation: 7361

Rails Devise - Create user and update associated record

Ruby version: 2.6.3

Rails version: 6.0

I have 2 models

1) User

has_one :address, dependent: :destroy
accept_nested_attributes :address

2) Address

belongs_to :user, optional: true

I used devise for user model. Address model has some records already but they aren't associated with the user. When user comes for registration that time i have to show the list of available addresses which are not associated with any user.

I wanted to use a nested form for this situation. I override devise registration controller, but attributes are not correctly in parameters.

here is my code of registration form

  <%= f.fields_for :address do |sc| %>
    <%= sc.label :address %>
    <%= sc.select :address, options_for_select(Address.available_addresses&.map{ |i| [i.address, i.id] }), class: 'form-control' %>
  <% end %>

parameters

Parameters: {"authenticity_token"=>"MN6zE3Z7B7zhQupa6kvbqGN4kmc8pAHRrdqtKipDclki3f6H5M919YCe9O7b+M8X49wuD3UczzpRJDl0Fmx0SQ==", "user"=>{"first"=>"daa", "last"=>"dasa", "email"=>"[email protected]", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]"}, "address_attributes"=>{"address"=>"1"}, "commit"=>"Sign up"}

application_controller.rb

before_action :configure_permitted_parameters, if: :devise_controller?

def configure_permitted_parameters
    devise_parameter_sanitizer.permit(:sign_up, keys: [:first, :last, address_attributes: [:id, :address]] )
  end

the main thing is it is not passing as attributes. it is passing only as address: [:address] . if i write f.fields_for :address it is not showing in view. but if i write fields_for :address than only it is showing in form. its quite weird why it is not working as expected.

I don't have to create a new record of address. How can I associate existing record of has one to a user?

Update

When I use address attributes it is just creating a new record for the address instead of updating address record for the user. it is quite strange for me why it is not updating record and inserting a new one. I just wanted to update user id of an address record. I don't want to create a new record of address model.

Update I resolve my issue with below code

new.html.erb

  <%= fields_for :address do |sc| %>
    <%= sc.label :address %>
    <%= sc.select :address, options_for_select(Address.available_addresses&.map{ |i| [i.address, i.id] }), class: 'form-control' %>
  <% end %>

users/registrations_controller.rb
if resource.persisted?
  if resource.active_for_authentication?
     address = Address.find(params[:address][:address])
     address.update_column(:user_id, resource.id)

I really wanted to know why this is happening, I used nested attributes, but it was just creating new record rather creating new user and updating address. it is a very small issue I wanted to figure it out what is happening behind the scenes.

Upvotes: 1

Views: 1359

Answers (2)

Violeta
Violeta

Reputation: 710

What helped me:

  1. In user.rb, override the address_attributes= method with:
def address_attributes=(address_attrs)
  self.address = Address.find_or_initialize_by(id: address_attrs[:id])
  self.address.attributes = address_attrs
end
  1. In the registration form:
<%= f.fields_for :address, resource.address ? resource.address : resource.build_address do |address_form| %>
    <%= address_form.label :id, "Address" %>
    <%= address_form.collection_select :id, Address.where(user_id: nil), :id, :address, include_blank: "Choose address", class: "form-control" %>
<% end %>
  1. Registrations Controller:
def configure_sign_up_params
  devise_parameter_sanitizer.permit(:sign_up, keys: [:name, address_attributes: [:id]])
end

References:

https://stackoverflow.com/a/9864783/11883546

https://stackoverflow.com/a/48014151/11883546

Upvotes: 1

Mosaaleb
Mosaaleb

Reputation: 1089

You need to generate devise controllers and configure the registrations_controller.rb Configure devise controllers since you need to build the address after creating new user.

rails g devise:controllers users -c=registrations

And add the controllers to the devise_for routes: For example: devise_for :users, controllers: { registrations: 'users/registrations' }

Then you can edit sign_up_params to include address_parameters

def sign_up_params
  params.require(:user).permit(:first, :last, address_attributes: [:your_address_attributes])
end

Also you need to override new in registrations_controller to include building the address

def new
  super { resource.build_address }
end

To know how this works, you need to have a look at registerations_controller

for the views part you still need the f.fields_for :address

Hope this helps.

Upvotes: 1

Related Questions