Reputation: 1276
I'm currently using Rails 4.2 with these models:
class User < ActiveRecord::Base
has_many :user_accessories, dependent: :destroy
accepts_nested_attributes_for :user_accessories,
reject_if: :all_blank,
allow_destroy: true
has_many :accessories, through: :user_accessories
end
class UserAccessory < ActiveRecord::Base
belongs_to :user, required: true
belongs_to :accessory, required: true
end
class Accessory < ActiveRecord::Base
has_many :user_accessories, dependent: :destroy
has_many :users, through: :user_accessories
end
This is a simple many-to-many relationship, where a user can "own" many accessories, and an accessory can be "owned" by many users.
I've set up my routes in the following way:
resources :users, only: [ :index, :show, :edit, :update, :accessories ] do
member do
get 'accessories'
end
end
This way, a user can go to their "accessories" path (/users/1/accessories
) to see a list of their accessories, as well as to add new accessory associations to their account.
Here's the view that renders for the accessories path:
<table>
<thead>
<tr>
<th>Name</th>
</tr>
</thead>
<tbody>
<%= render @accessories %>
</tbody>
</table>
<%= form_for( @user ) do |f| %>
<%= f.collection_select(
:accessory_ids,
Accessory.all,
:id,
:name, { include_hidden: false }, { multiple: true } )
%>
<%= f.button "Add Equipment" %>
<% end %>
This just renders a table of current accessories, and then builds a collection_select box where you can choose multiple accessories to add. I've simplified this by removing Bootstrap styling.
Finally, here are the relevant controller actions:
def update
@user = User.find( params[:id] )
if( @user.update_attributes( update_params ) )
redirect_to @user
else
render 'edit'
end
end
def accessories
@user = User.find( params[:id] )
@accessories = @user.accessories
end
private
def update_params
params.require( :user ).permit(
:first_name,
:last_name,
:region_id,
:country,
:profile_image,
:remove_profile_image,
accessory_ids: [:id],
)
end
Now the Problems:
Submitting the form when I already have a value manually entered in the database actually just deletes all currently stored values and does not add any new ones:
Started PATCH "/users/nuggles" for 127.0.0.1 at 2015-02-18 13:31:50 -0500
Processing by UsersController#update as HTML
Parameters: {
"utf8"=>"✓",····
"authenticity_token"=>"my_token",·
"user"=>{
"accessory_ids"=>["5"]
},·
"button"=>"",·
"id"=>"nuggles"
}
User Load (40.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT 1 [["id", 4]]
User Load (36.4ms) SELECT "users".* FROM "users" WHERE "users"."slug" = $1 ORDER BY "users"."id" ASC LIMIT 1 [["slug", "nuggles"]]·
(33.5ms) BEGIN
Accessory Load (34.1ms) SELECT "accessories".* FROM "accessories" INNER JOIN "user_accessories" ON "accessories"."id" = "user_accessories"."accessory_id" WHERE "user_accessories"."user_id" = $1 [["user_id", 4]]
**SQL (34.1ms) DELETE FROM "user_accessories" WHERE "user_accessories"."user_id" = $1 AND "user_accessories"."accessory_id" IN (1, 2, 3) [["user_id", 4]]**
User Exists (37.7ms) SELECT 1 AS one FROM "users" WHERE (LOWER("users"."username") = LOWER('Nuggles') AND "users"."id" != 4) LIMIT 1
(34.2ms) COMMIT
Redirected to http://localhost:3000/users/nuggles
Completed 302 Found in 304ms (ActiveRecord: 250.0ms)
When submitting the form with nothing selected, I get the following error:
param is missing or the value is empty: user
private
def update_params
params.require( :user ).permit(
:first_name,
:last_name,
:region_id,
Upvotes: 0
Views: 1179
Reputation: 1276
For anyone who is interested, I've got the form working. This may not be the ideal way to create multiple records at one time, but it's working for me right now.
First, I took out the weird accessories
definition in the Users
controller. Since I was going to be directly creating, updating, and destroying a user's accessories, the join model deserved its own controller:
class UserAccessoriesController < ApplicationController
def index
@user = User.friendly.find( params[:id] )
@user_accessories = @user.user_accessories
end
def create
@user = User.friendly.find( params[:id] )
create_params[:accessory].each do |accessory|
@user_accessory = UserAccessory.new( user: @user, accessory_id: accessory )
@user_accessory.save
end
redirect_to user_accessories_path( @user )
end
def destroy
@user_accessory = UserAccessory.friendly.find( params[:id] )
@user_accessory.destroy
end
private
def create_params
params.require( :user_accessory ).permit(
:id,
accessory: []
)
end
end
I only have one view for UserAccessories
, the index. This page lists all of a user's accessories, and if the displayed user is the same as the logged in user, the following form is displayed:
<%= form_for @user_accessories.new(), { method: :post } do |f| %>
<div class="form-group">
<%= f.collection_select(
:accessory,
# Get only accessories not on the user's account
Accessory.where.not( id: @user.accessories ),
:id,
:name,
{
include_hidden: false
},
{
multiple: true,
class: 'form-control',
placeholder: 'Select Equipment to Add'
}
) %>
</div>
<div class="form-group">
<%= f.submit "Update Equipment" %>
</div>
<% end %>
This allows the user to select multiple accessories (using select2), then on submit creates the association one by one in the controller.
If anyone has a better method for the creation of the user accessories (i.e. not looping through my params and creating that many times), then please leave a comment!
Upvotes: 1