I have following Models
User
has_many :users_contacts
has_many :contacts, through: :users_contacts
accepts_nested_attributes_for :contacts, allow_destroy: true
Contact
has_many :users_contacts
has_many :users, through: :users_contacts
accepts_nested_attributes_for :users_contacts, allow_destroy: true
UsersContact
belongs_to :users
belongs_to :contacts
I am using following strong parameters
params.require(:user).permit(:id, :email,
contacts_attributes: [:id, :first_name, :last_name,
users_contacts_attributes: [:id, :contact_id, :user_id, :order]])
The problem i am facing is whenever i am updating users with users_contacts_attributes
like {contact_id: 5, order: 5}
It creates two records one with order: nil
& other with order: 5
i am getting order in params.
I want following
Don't want duplicate records to be created.
Save the record in joining table with extra column i.e. order
My params are something like following
{"id"=>4,
"email"=>"[email protected]",
"contacts_attributes"=>
[{"id"=>150,
"first_name"=>"Pqr",
"is_shareable"=>true,
"users_contacts_attributes"=>[{"id"=>87, "user_id"=>4, "contact_id"=>150, "order"=>100}]
},
{"first_name"=>"Def",
"is_shareable"=>true,
"users_contacts_attributes"=>[{"user_id"=>4, "order"=>101}]
}
]
}
Not having seen your controllers or form, it's hard to determine what specifically is causing your problem. In addition to the comment from @surya above, I noticed that the two belongs_to
statements in your UsersContact
class should be singular, not plural (:user
, :contact
). Also, not sure you need to nest users_contacts_attributes
in contacts_attributes
which then makes it nested two deep for user
when you can just have a separate accepts_nested_attributes_for :users_contacts
in the User
class.
Beyond that, you'll have to compare your code to the code I have provided below to see if you can find where your trouble lies.
Note: The form is very basic and I have modified some names to indicate more clearly when singular and plural are used and for readability.
Models
class User < ActiveRecord::Base
has_many :user_contact_pairs, inverse_of: :user
has_many :contacts, through: :user_contact_pairs
accepts_nested_attributes_for :contacts, allow_destroy: true
accepts_nested_attributes_for :user_contact_pairs, allow_destroy: true
end
class Contact < ActiveRecord::Base
has_many :user_contact_pairs, inverse_of: :contact
has_many :users, through: :user_contact_pairs
accepts_nested_attributes_for :user_contact_pairs, allow_destroy: true
end
class UserContactPair < ActiveRecord::Base
belongs_to :contact
belongs_to :user
end
Controller
class UsersController < ApplicationController
before_action :set_user, only: [:show, :edit, :update, :destroy]
def index
@users = User.all
end
def show
end
def new
@user = User.new
end
def edit
@user.user_contact_pairs.build(user_id: @user.id)
end
def create
@user = User.new(user_params)
respond_to do |format|
if @user.save
format.html { redirect_to @user, notice: 'User was successfully created.' }
format.json { render :show, status: :created, location: @user }
else
format.html { render :new }
format.json { render json: @user.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if @user.update(user_params)
format.html { redirect_to @user, notice: 'User was successfully updated.' }
format.json { render :show, status: :ok, location: @user }
else
format.html { render :edit }
format.json { render json: @user.errors, status: :unprocessable_entity }
end
end
end
def destroy
@user.destroy
respond_to do |format|
format.html { redirect_to users_url, notice: 'User was successfully destroyed.' }
format.json { head :no_content }
end
end
private
def set_user
@user = User.find(params[:id])
end
def user_params
params.require(:user).
permit(:email, :first_name, :last_name, :username,
contacts_attributes:
[:id, :first_name, :last_name],
user_contact_pairs_attributes:
[:id, :contact_id, :user_id, :order_number])
end
end
View (users/index.html.erb)
<p id="notice"><%= notice %></p>
<h1>Listing Users</h1>
<table>
<thead>
<tr>
<th colspan="3"></th>
</tr>
</thead>
<tbody>
<tr>
<td><%= 'Id' %></td>
<td><%= 'First Name' %></td>
<td><%= 'Last Name' %></td>
<td><%= 'Username' %></td>
<td></td>
<td></td>
<td></td>
</tr>
<% @users.each do |user| %>
<tr>
<td><%= user.id %></td>
<td><%= user.first_name %></td>
<td><%= user.last_name %></td>
<td><%= user.username %></td>
<td><%= link_to 'Show', user %></td>
<td><%= link_to 'Edit', edit_user_path(user) %></td>
<td><%= link_to 'Destroy', user, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
</tbody>
</table>
<br>
<%= link_to 'New User', new_user_path %>
View (users/edit.html.erb)
<h1>Editing User</h1>
<%= render 'form' %>
<%= link_to 'Show', @user %> |
<%= link_to 'Back', users_path %>
View (users/_form.html.erb)
<%= form_for(@user) do |user_form| %>
<% if @user.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(@user.errors.count, "error") %> prohibited this user from being saved:</h2>
<ul>
<% @user.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= user_form.label :email %>
<%= user_form.email_field :email %><br>
<%= user_form.label :first_name %>
<%= user_form.text_field :first_name %><br>
<%= user_form.label :last_name %>
<%= user_form.text_field :last_name %><br>
<%= user_form.label :username %>
<%= user_form.text_field :username %><br>
</div>
<h2>User Contact Pair</h2>
<%= user_form.fields_for :user_contact_pairs do |ucp_fields| %>
<div class="user-contact-pair">
<div class="field">
<%= ucp_fields.label :contact_id %>
<%= ucp_fields.number_field :contact_id %><br>
<%= ucp_fields.label :order_number %>
<%= ucp_fields.number_field :order_number %><br>
</div>
</div>
<% end %>
<div class="actions">
<%= user_form.submit %>
</div>
<% end %>
Migrations
class CreateUser < ActiveRecord::Migration
def change
create_table :users do |t|
t.string :email, index: { unique: true }
t.string :first_name
t.string :last_name
t.string :username, index: { unique: true }
t.timestamps null: false
end
end
end
class CreateContact < ActiveRecord::Migration
def change
create_table :contacts do |t|
t.string :first_name
t.string :last_name
t.timestamps null: false
end
end
end
class CreateUserContactPair < ActiveRecord::Migration
def change
create_table :user_contact_pairs do |t|
t.integer :contact_id
t.integer :user_id
t.integer :order_number
t.timestamps null: false
end
end
end
DB Seed File
User.create(email: '[email protected]', first_name: 'Lisa', last_name: 'Hawkins', username: 'lhawkinsa')
User.create(email: '[email protected]', first_name: 'Helen', last_name: 'Taylor', username: 'htaylorb')
User.create(email: '[email protected]', first_name: 'Gregory', last_name: 'Taylor', username: 'gtaylorc')
User.create(email: '[email protected]', first_name: 'Henry', last_name: 'Lane', username: 'hlaned')
User.create(email: '[email protected]', first_name: 'Harry', last_name: 'Phillips', username: 'hphillipse')
User.create(email: '[email protected]', first_name: 'Jeffrey', last_name: 'Gonzales', username: 'jgonzalesf')
User.create(email: '[email protected]', first_name: 'Lori', last_name: 'James', username: 'ljamesg')
User.create(email: '[email protected]', first_name: 'Roger', last_name: 'Hill', username: 'rhillh')
User.create(email: '[email protected]', first_name: 'Raymond', last_name: 'Harvey', username: 'rharveyi')
User.create(email: '[email protected]', first_name: 'Stephen', last_name: 'Perry', username: 'sperryj')
Contact.create(first_name: 'Louis', last_name: 'Harris')
Contact.create(first_name: 'Fred', last_name: 'Adams')
Contact.create(first_name: 'David', last_name: 'Lane')
Contact.create(first_name: 'Kevin', last_name: 'Ryan')
Contact.create(first_name: 'Samuel', last_name: 'Jones')
Rails Console (after two user updates via form)
Running via Spring preloader in process 30656
Loading development environment (Rails 4.2.7.1)
2.3.3 :001 > UserContactPair.all
UserContactPair Load (0.7ms) SELECT "user_contact_pairs".* FROM "user_contact_pairs"
=> #<ActiveRecord::Relation [#<UserContactPair id: 1, contact_id: 1, user_id: 1, order_number: 1, created_at: "2017-02-14 09:05:31", updated_at: "2017-02-14 09:05:31">, #<UserContactPair id: 2, contact_id: 4, user_id: 1, order_number: 4, created_at: "2017-02-14 09:05:50", updated_at: "2017-02-14 09:05:50">]>
2.3.3 :002 >
UPDATE (in response to comment):
Here is the updated code that should do what you are asking. You should look to add validations that confirm presence and uniqueness where necessary in the above code and within this additional code. You many also want to sort the Contact
collection used to populate the form select drop-down.
(Note: Due to the fact that the use of nested attributes causes the addition of number keys (e.g. ["0"]) within param hashes, which vary depending upon the number of associated records of those nested attribute classes an object has, I've added the hashie gem to the gem file, which provides the ability to find deeply nested keys within a hash via .deep_find
, enabling direct access to the desired keys, bypassing these varying number keys.)
Models (added method to Contact class for form select drop-down)
def last_name_first
self.last_name + ', ' + self.first_name
end
Controller (edit and update actions updated)
def edit
@contacts = Contact.all
@user.contacts.build
@user.user_contact_pairs.build
end
def update
@user = User.find(params[:id])
user_only_params = { email: params[:user][:email], first_name: params[:user][:first_name], last_name: params[:user][:last_name], username: params[:user][:username] }
contact_params = params[:user][:contacts_attributes]
user_contact_pair_params = params[:user][:user_contact_pairs_attributes]
contact_params.extend(Hashie::Extensions::DeepFind)
contact_first_name = contact_params.deep_find(:first_name)
contact_last_name = contact_params.deep_find(:last_name)
user_contact_pair_params.extend(Hashie::Extensions::DeepFind)
user_contact_pair_contact_id = user_contact_pair_params.deep_find(:contact_id)
user_contact_pair_order_number = user_contact_pair_params.deep_find(:order_number)
@user.assign_attributes(user_only_params)
if user_contact_pair_order_number != ''
if contact_first_name != '' && contact_last_name != ''
@contact = Contact.create(first_name: contact_first_name, last_name: contact_last_name)
@user.user_contact_pairs.new(contact_id: @contact.id, order_number: user_contact_pair_order_number.to_i)
elsif user_contact_pair_contact_id != ''
@user.user_contact_pairs.new(contact_id: user_contact_pair_contact_id.to_i, order_number: user_contact_pair_order_number.to_i)
end
end
respond_to do |format|
if @user.save
format.html { redirect_to @user, notice: 'User was successfully updated.' }
format.json { render :show, status: :ok, location: @user }
else
@contact.destroy if @contact.exists?
format.html { render :edit }
format.json { render json: @user.errors, status: :unprocessable_entity }
end
end
end
View (users/_form.html.erb)
<%= form_for(@user) do |user_form| %>
<% if @user.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(@user.errors.count, "error") %> prohibited this user from being saved:</h2>
<ul>
<% @user.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="user">
<h2 style="margin-bottom: 4px">User</h2>
<%= user_form.label :email %><br>
<%= user_form.email_field :email %><br><br>
<%= user_form.label :first_name %><br>
<%= user_form.text_field :first_name %><br><br>
<%= user_form.label :last_name %><br>
<%= user_form.text_field :last_name %><br><br>
<%= user_form.label :username %><br>
<%= user_form.text_field :username %><br><br>
</div>
<%= user_form.fields_for :user_contact_pairs do |ucp_fields| %>
<% if ucp_fields.object.new_record? %>
<h2 style="margin-bottom: 4px">Use Existing Contact</h2>
<%= ucp_fields.collection_select(:contact_id, @contacts, :id, :last_name_first, prompt: "Select...") %><br><br>
<% end %>
<% end %>
<%= user_form.fields_for :contacts do |contact_fields| %>
<% if contact_fields.object.new_record? %>
<h2 style="margin-bottom: 4px">Or Create New Contact</h2>
<%= contact_fields.label 'First Name' %><br>
<%= contact_fields.text_field :first_name %><br><br>
<%= contact_fields.label 'Last Name' %><br>
<%= contact_fields.text_field :last_name %><br><br>
<% end %>
<% end %>
<%= user_form.fields_for :user_contact_pairs do |ucp_fields| %>
<% if ucp_fields.object.new_record? %>
<h2 style="margin-bottom: 4px">Order</h2>
<%= ucp_fields.label 'Order Number' %><br>
<%= ucp_fields.number_field :order_number %><br><br>
<% end %>
<% end %>
<div class="actions">
<%= user_form.submit %>
</div>
<% end %>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With