collection_select 与 has_many 和 belongs_to
collection_select with has_many and belongs_to
我有一个简单的脚手架项目来测试此功能。
它有两个模型,User
模型和Fruit
模型。
我正在尝试实现一项功能,用户可以使用 collection_select 从数据库中选择现有水果,然后将其列在他的名字旁边,还可以根据水果查询用户(EG : 列出所有拥有苹果的用户)
注意:我在水果table中装满了样品水果来测试一下。不确定这是否是正确的方法,但我让 collection_select 工作了。
我的问题
问题有两个方面。一个水果没有保存给特定用户(即使参数具有必要的属性),第二个如果我尝试调用 User.fruits.name
我得到 "Fruits" 和 User.fruits
给我一个意想不到的结果的“#”。
我一直在阅读并尝试我的主要项目的解决方案,该项目具有精确的结构,但它似乎不起作用。如果有更高效的版本,我也在寻找替代方案。
以下是代码片段:
参数散列
class User < ActiveRecord::Base
has_many :fruits
end
users/index.html.erb
<p id="notice"><%= notice %></p>
<h1>Listing Users</h1>
<table>
<thead>
<tr>
<th>Name</th>
<th colspan="3"></th>
</tr>
</thead>
<tbody>
<% @users.each do |user| %>
<tr>
<td><%= user.name %></td>
<<td><%= user.fruits%></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 %>
users/_form.html.erb
<%= form_for(@user) do |f| %>
<% 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">
<%= f.label :name %><br>
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :fruits %>
<%= f.collection_select(:fruits, Fruit.all, :id, :name, :include_blank => "Please select") %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
users_controller
class UsersController < ApplicationController
before_action :set_user, only: [:show, :edit, :update, :destroy]
# GET /users
# GET /users.json
def index
@users = User.all
end
# GET /users/1
# GET /users/1.json
def show
end
# GET /users/new
def new
@fruit = Fruit.new
@user = User.new
end
# GET /users/1/edit
def edit
end
# POST /users
# POST /users.json
def create
debugger
@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
# PATCH/PUT /users/1
# PATCH/PUT /users/1.json
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
# DELETE /users/1
# DELETE /users/1.json
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
# Use callbacks to share common setup or constraints between actions.
def set_user
@user = User.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def user_params
params.require(:user).permit(:name, :fruit_ids)
end
end
fruit.rb
class Fruit < ActiveRecord::Base
belongs_to :user
end
user.rb
class User < ActiveRecord::Base
has_many :fruits
end
schema.rb
ActiveRecord::Schema.define(version: 20151215214130) do
create_table "fruits", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "user_id"
end
create_table "users", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
end
老实说,我已经无计可施了。我觉得我错过了一些非常基本和明显的东西。
好像对user.fruits
是什么有误解。 user 上的 fruits collection 本质上是一个 Fruit object 数组。你不想这样做
= @user.fruits
或这个
= User.fruits # I don't think this method on the `User` class exists
也许你想这样做
= @user.fruits.map{|f| f.name}
或这个
= render @user.fruits
后者将调用 _fruit.erb 部分并根据其中的代码进行渲染。
# _fruit.erb
= fruit.name
要设置user.fruits,您需要在数据库中进行设置。有两种方法可以做到这一点
1) 将水果id发送给用户#fruit_ids=方法
# for example
@user.fruit_ids = [1,2,3,4]
2) 在水果上设置user_idobject
@fruit.user = @user # or @fruit.user_id = @user.id
您似乎想将 fruit_ids 传递给用户。为此,一种常见的方法是设置一个复选框网格。他们需要有参数名称 "user[fruit_ids][]"。末尾多出的[]是为了向rails表明这是一个数组。
另一种方法,也许是您想要的,是设置多个 select 框
<%= f.collection_select(:fruit_ids, Fruit.all, :id, :name, { :include_blank => "Please select")}, { :multiple => true } %>
在控制器中,确保强参数接受这个参数——语法是这样的
params.require(:user).permit(:name, :fruit_ids => [])
然后您可以将 id 数组传递给控制器,它会在所有水果 object 上正确设置 user_id。
最后 - 我不确定你在做什么是你想做的。 has_many、belongs_to 关系通常是 parent-child 关系。我看不到您提前设置水果然后将其分配给用户的情况。也许这只是一个练习?另一种在水果上设置 user_id 的方法 - 您可以在用户下创建水果。
@user.fruits << Fruit.new(:name => "Orange")
如果你想要一个水果object(系统中只有一个橙子或一个苹果)用户可以收藏它或select它而不编辑水果记录,你会这样做。
有另一个 table 代表最喜欢的水果,看起来像这样:
class UserFruit < ActiveRecord::Base
belongs_to :user
belongs_to :fruit
end
使用此模型,您可以代表所有权或最喜欢的水果并重复使用水果 object,这样其他用户也可以喜欢或拥有它。
你可以这样设置
class User < ActiveRecord::Base
has_many :user_fruits, :dependent => :destroy
has_many :fruits, :through => :user_fruits
end
并更改fruit.rb
class Fruit < ActiveRecord::Base
has_many :user_fruits, :dependent => :destroy
has_many :users, :through => :user_fruits
end
您仍然可以使用 @user.fruit_ids=(array)
方法来 create/destroy 加入 table 记录,您的用户表单根本不会改变。
别忘了迁移
create_table :user_fruits do |t|
t.references :user
t.references :fruit
end
我有一个简单的脚手架项目来测试此功能。
它有两个模型,User
模型和Fruit
模型。
我正在尝试实现一项功能,用户可以使用 collection_select 从数据库中选择现有水果,然后将其列在他的名字旁边,还可以根据水果查询用户(EG : 列出所有拥有苹果的用户)
注意:我在水果table中装满了样品水果来测试一下。不确定这是否是正确的方法,但我让 collection_select 工作了。
我的问题
问题有两个方面。一个水果没有保存给特定用户(即使参数具有必要的属性),第二个如果我尝试调用 User.fruits.name
我得到 "Fruits" 和 User.fruits
给我一个意想不到的结果的“#”。
我一直在阅读并尝试我的主要项目的解决方案,该项目具有精确的结构,但它似乎不起作用。如果有更高效的版本,我也在寻找替代方案。
以下是代码片段:
参数散列
class User < ActiveRecord::Base
has_many :fruits
end
users/index.html.erb
<p id="notice"><%= notice %></p>
<h1>Listing Users</h1>
<table>
<thead>
<tr>
<th>Name</th>
<th colspan="3"></th>
</tr>
</thead>
<tbody>
<% @users.each do |user| %>
<tr>
<td><%= user.name %></td>
<<td><%= user.fruits%></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 %>
users/_form.html.erb
<%= form_for(@user) do |f| %>
<% 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">
<%= f.label :name %><br>
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :fruits %>
<%= f.collection_select(:fruits, Fruit.all, :id, :name, :include_blank => "Please select") %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
users_controller
class UsersController < ApplicationController
before_action :set_user, only: [:show, :edit, :update, :destroy]
# GET /users
# GET /users.json
def index
@users = User.all
end
# GET /users/1
# GET /users/1.json
def show
end
# GET /users/new
def new
@fruit = Fruit.new
@user = User.new
end
# GET /users/1/edit
def edit
end
# POST /users
# POST /users.json
def create
debugger
@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
# PATCH/PUT /users/1
# PATCH/PUT /users/1.json
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
# DELETE /users/1
# DELETE /users/1.json
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
# Use callbacks to share common setup or constraints between actions.
def set_user
@user = User.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def user_params
params.require(:user).permit(:name, :fruit_ids)
end
end
fruit.rb
class Fruit < ActiveRecord::Base
belongs_to :user
end
user.rb
class User < ActiveRecord::Base
has_many :fruits
end
schema.rb
ActiveRecord::Schema.define(version: 20151215214130) do
create_table "fruits", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "user_id"
end
create_table "users", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
end
老实说,我已经无计可施了。我觉得我错过了一些非常基本和明显的东西。
好像对user.fruits
是什么有误解。 user 上的 fruits collection 本质上是一个 Fruit object 数组。你不想这样做
= @user.fruits
或这个
= User.fruits # I don't think this method on the `User` class exists
也许你想这样做
= @user.fruits.map{|f| f.name}
或这个
= render @user.fruits
后者将调用 _fruit.erb 部分并根据其中的代码进行渲染。
# _fruit.erb
= fruit.name
要设置user.fruits,您需要在数据库中进行设置。有两种方法可以做到这一点
1) 将水果id发送给用户#fruit_ids=方法
# for example
@user.fruit_ids = [1,2,3,4]
2) 在水果上设置user_idobject
@fruit.user = @user # or @fruit.user_id = @user.id
您似乎想将 fruit_ids 传递给用户。为此,一种常见的方法是设置一个复选框网格。他们需要有参数名称 "user[fruit_ids][]"。末尾多出的[]是为了向rails表明这是一个数组。
另一种方法,也许是您想要的,是设置多个 select 框
<%= f.collection_select(:fruit_ids, Fruit.all, :id, :name, { :include_blank => "Please select")}, { :multiple => true } %>
在控制器中,确保强参数接受这个参数——语法是这样的
params.require(:user).permit(:name, :fruit_ids => [])
然后您可以将 id 数组传递给控制器,它会在所有水果 object 上正确设置 user_id。
最后 - 我不确定你在做什么是你想做的。 has_many、belongs_to 关系通常是 parent-child 关系。我看不到您提前设置水果然后将其分配给用户的情况。也许这只是一个练习?另一种在水果上设置 user_id 的方法 - 您可以在用户下创建水果。
@user.fruits << Fruit.new(:name => "Orange")
如果你想要一个水果object(系统中只有一个橙子或一个苹果)用户可以收藏它或select它而不编辑水果记录,你会这样做。
有另一个 table 代表最喜欢的水果,看起来像这样:
class UserFruit < ActiveRecord::Base
belongs_to :user
belongs_to :fruit
end
使用此模型,您可以代表所有权或最喜欢的水果并重复使用水果 object,这样其他用户也可以喜欢或拥有它。
你可以这样设置
class User < ActiveRecord::Base
has_many :user_fruits, :dependent => :destroy
has_many :fruits, :through => :user_fruits
end
并更改fruit.rb
class Fruit < ActiveRecord::Base
has_many :user_fruits, :dependent => :destroy
has_many :users, :through => :user_fruits
end
您仍然可以使用 @user.fruit_ids=(array)
方法来 create/destroy 加入 table 记录,您的用户表单根本不会改变。
别忘了迁移
create_table :user_fruits do |t|
t.references :user
t.references :fruit
end