Rails 4.2 - 如何建立一个has_many,通过:关联collection_select
Rails 4.2 - how to build a has_many, through: association with collection_select
我目前在这些型号上使用 Rails 4.2:
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
这是一个简单的多对多关系,一个用户可以"own"个配件,一个配件可以"owned"个用户。
我按以下方式设置了路线:
resources :users, only: [ :index, :show, :edit, :update, :accessories ] do
member do
get 'accessories'
end
end
这样,用户可以转到他们的 "accessories" 路径 (/users/1/accessories
) 查看他们的配件列表,以及将新的配件关联添加到他们的帐户。
这是为附件路径呈现的视图:
<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 %>
这只是渲染一个 table 当前配件,然后构建一个 collection_select 框,您可以在其中选择要添加的多个配件。我通过删除 Bootstrap 样式简化了这一点。
最后,这里是相关的控制器动作:
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
现在的问题:
当我已经在数据库中手动输入了一个值时提交表单实际上只会删除所有当前存储的值并且不会添加任何新值:
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" = ORDER BY "users"."id" ASC LIMIT 1 [["id", 4]]
User Load (36.4ms) SELECT "users".* FROM "users" WHERE "users"."slug" = 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" = [["user_id", 4]]
**SQL (34.1ms) DELETE FROM "user_accessories" WHERE "user_accessories"."user_id" = 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)
在未选择任何内容的情况下提交表单时,出现以下错误:
param is missing or the value is empty: user
private
def update_params
params.require( :user ).permit(
:first_name,
:last_name,
:region_id,
对于任何感兴趣的人,我的表格都可以使用。这可能不是一次创建多个记录的理想方式,但它现在对我有用。
首先,我在Users
控制器中取出了奇怪的accessories
定义。因为我打算直接创建、更新和销毁用户的配件,所以连接模型应该有自己的控制器:
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
索引 UserAccessories
我只有一个视图。该页面列出了用户的所有配件,如果显示的用户与登录用户相同,则显示如下形式:
<%= 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 %>
这允许用户 select 多个附件(使用 select2),然后在提交时在控制器中一一创建关联。
如果有人有更好的方法来创建用户配件(即不循环遍历我的参数并创建那么多次),请发表评论!
我目前在这些型号上使用 Rails 4.2:
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
这是一个简单的多对多关系,一个用户可以"own"个配件,一个配件可以"owned"个用户。
我按以下方式设置了路线:
resources :users, only: [ :index, :show, :edit, :update, :accessories ] do
member do
get 'accessories'
end
end
这样,用户可以转到他们的 "accessories" 路径 (/users/1/accessories
) 查看他们的配件列表,以及将新的配件关联添加到他们的帐户。
这是为附件路径呈现的视图:
<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 %>
这只是渲染一个 table 当前配件,然后构建一个 collection_select 框,您可以在其中选择要添加的多个配件。我通过删除 Bootstrap 样式简化了这一点。
最后,这里是相关的控制器动作:
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
现在的问题:
当我已经在数据库中手动输入了一个值时提交表单实际上只会删除所有当前存储的值并且不会添加任何新值:
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" = ORDER BY "users"."id" ASC LIMIT 1 [["id", 4]] User Load (36.4ms) SELECT "users".* FROM "users" WHERE "users"."slug" = 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" = [["user_id", 4]] **SQL (34.1ms) DELETE FROM "user_accessories" WHERE "user_accessories"."user_id" = 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)
在未选择任何内容的情况下提交表单时,出现以下错误:
param is missing or the value is empty: user private def update_params params.require( :user ).permit( :first_name, :last_name, :region_id,
对于任何感兴趣的人,我的表格都可以使用。这可能不是一次创建多个记录的理想方式,但它现在对我有用。
首先,我在Users
控制器中取出了奇怪的accessories
定义。因为我打算直接创建、更新和销毁用户的配件,所以连接模型应该有自己的控制器:
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
索引 UserAccessories
我只有一个视图。该页面列出了用户的所有配件,如果显示的用户与登录用户相同,则显示如下形式:
<%= 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 %>
这允许用户 select 多个附件(使用 select2),然后在提交时在控制器中一一创建关联。
如果有人有更好的方法来创建用户配件(即不循环遍历我的参数并创建那么多次),请发表评论!