'Rails: How to save attribute for parent User (current_user) when submitting form to create child object?
I have a User model (using Devise) and a Listing model. User has_many :listings and Listing belongs_to :user.
The user has email, password and phone number attributes, but when creating an account, only email and password are required. Phone number can be left blank.
When a logged-in user creates a Listing, I'd like to provide the option for the current user to include his phone number using the same form. The expectation is to store the phone number of the user who is creating the listing.
I've created forms to accept nested attributes for child objects before, but not for parent object like this case. I haven't figured out how to get the form to do this, and whether I need to change something in the controller or models. Any help is appreaciated.
User model
class User < ApplicationRecord
has_many :listings, dependent: :destroy
end
Listing model
class Listing < ApplicationRecord
belongs_to :user
end
Listings controller
def create
@listing = current_user.listings.build(listing_params)
respond_to do |format|
if @listing.save
format.html { redirect_to listing_url(@listing), notice: "Listing was successfully created." }
format.json { render :show, status: :created, location: @listing }
else
...
end
end
end
New Listing Form
<%= form_with model: @listing do |f| %>
<div class="field">
<%= f.label :title, class: "label required" %>
<div class="control">
<%= f.text_field :title, autofocus: true, autocomplete: "title", required: true, class: "input" %>
</div>
</div>
...
<% end>
Thanks
Solution 1:[1]
For save the phone at the user, you can do something like that in the controller:
def create
@listing = current_user.listings.build(listing_params)
user = current_user
user.phone = params[:phone]
respond_to do |format|
if @listing.save && user.save
format.html { redirect_to listing_url(@listing), notice: "Listing was successfully created." }
format.json { render :show, status: :created, location: @listing }
else
...
end
end
end
Solution 2:[2]
My solution is a little overcomplicated, but it uses the standard nested attributes and strong params. It will also prepopulate the phone input if the data is available.
Enable nested_attributes on Listing.user. Limit it to update_only since you don't want to be able to create a user in this way.
class Listing < ApplicationRecord
belongs_to :user
accepts_nested_attributes_for :user, update_only: true
end
Add the following nested form to the Listing form partial. Note that include_id is set to false so that the User's ID is not exposed in the form.
<%= f.fields_for :user, include_id: false do |uf| %>
<div class="field">
<%= uf.label :phone, class: "label required" %>
<div class="control">
<%= uf.text_field :phone, autofocus: true,
autocomplete: "title", required: true, class: "input" %>
</div>
</div>
<% end %>
Permit the nested user params in the controller:
def listing_params
params.fetch(:listing, {}).permit(:title, user_attributes: [:phone])
end
Initialize the Listing with a user in the new action of the controller.
def new
@listing = Listing.new(user: @current_user)
end
Finally, in your create action, you'll need to merge the user id into the params, since it wasn't included with the form submission. The user id has to be set both in the nested attributes, and on the new listing record.
def create
@listing = Listing.new(
listing_params.to_h.deep_merge(
user_id: @current_user.id,
user_attributes: { id: @current_user.id }
))
...
end
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Matteo |
| Solution 2 | TonyArra |
