Rails 5 - has_many 通过和嵌套属性形式
Rails 5 - has_many through and nested attributes forms
我有两个模型 Request 和 TableLocation 都具有 has_many 到 由 RequestLocation table.
加入的关系
我正在尝试创建嵌套表单,但 table_location 数据未保存到数据库中。
如您所见,table_locations"=>["1"] 参数正在传递给创建操作但未保存。
感谢任何帮助。
控制台输出
Started POST "/requests" for 127.0.0.1 at 2017-06-07 10:35:26 -0400
Processing by RequestsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"mHV/xbmdfHmCAsi16KXlW+0bWVSkEo9SRVchdyPpL60o3m3SuKEt4nuUT4PJNEyCsWq3Nj4IWiCMlDbhiPewdA==", "request"=>{"concierge_name"=>"Alex", "concierge_number"=>"954-123-4567", "concierge_email"=>"alex@email.com", "client_name"=>"Adam", "client_number"=>"954-765-4321", "client_email"=>"adam@email.com", "hotel_employee"=>"0", "concierge_service"=>"0", "vip_promoter"=>"0", "arriving_with_client"=>"1", "client_alone"=>"0", "males"=>"", "females"=>"1", "table_minimum"=>"1000", "arrival_time(1i)"=>"2017", "arrival_time(2i)"=>"6", "arrival_time(3i)"=>"7", "arrival_time(4i)"=>"14", "arrival_time(5i)"=>"35", "table_locations"=>["1"], "comments"=>""}, "commit"=>"Submit"}
(0.1ms) BEGIN
SQL (0.4ms) INSERT INTO "requests" ("concierge_name", "concierge_number", "concierge_email", "client_name", "client_number", "client_email", "arriving_with_client", "people", "females", "table_minimum", "arrival_time", "comments", "created_at", "updated_at") VALUES (, , , , , , , , , , , , , ) RETURNING "id" [["concierge_name", "Alex"], ["concierge_number", "954-123-4567"], ["concierge_email", "alex@email.com"], ["client_name", "Adam"], ["client_number", "954-765-4321"], ["client_email", "adam@email.com"], ["arriving_with_client", "t"], ["people", 1], ["females", 1], ["table_minimum", 1000], ["arrival_time", "2017-06-07 14:35:00"], ["comments", ""], ["created_at", "2017-06-07 14:35:26.658718"], ["updated_at", "2017-06-07 14:35:26.658718"]]
(0.3ms) COMMIT
Redirected to http://localhost:3000/thanks
Completed 302 Found in 8ms (ActiveRecord: 0.8ms)
app/models/request.rb
class Request < ApplicationRecord
has_many :request_locations
has_many :table_locations, through: :request_locations
end
app/models/table_locations.rb
class TableLocation < ApplicationRecord
has_many :request_locations
has_many :requests, through: :request_locations
end
app/models/request_location.rb
class RequestLocation < ApplicationRecord
belongs_to :request
belongs_to :table_location
end
app/controllers/requests_controller.rb
class RequestsController < ApplicationController
before_action :set_request, only: [:show,
:edit,
:update,
:destroy]
before_action :authenticate_admin!, except: [:index,
:new,
:create]
def index
redirect_to root_path unless admin_signed_in?
@requests = Request.search(params[:term], params[:filter], params[:page])
end
def show
end
def new
@request = Request.new
end
def edit
end
def create
@request = Request.new(request_params)
@request.people = (@request.males || 0) + (@request.females || 0)
respond_to do |format|
if @request.save
format.html { redirect_to thanks_path, notice: 'Request was successfully created.' }
format.json { render :show, status: :created, location: @request }
else
format.html { render :new }
format.json { render json: @request.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if @request.update(request_params)
format.html { redirect_to @request, notice: 'Request was successfully updated.' }
format.json { render :show, status: :ok, location: @request }
else
format.html { render :edit }
format.json { render json: @request.errors, status: :unprocessable_entity }
end
end
end
def destroy
@request.destroy
respond_to do |format|
format.html { redirect_to requests_url, notice: 'Request was successfully destroyed.' }
format.json { head :no_content }
end
end
private
def set_request
@request = Request.find(params[:id])
end
def request_params
params.require(:request).permit(:concierge_name,
:concierge_number,
:concierge_email,
:client_name,
:client_number,
:client_email,
:hotel_employee,
:concierge_service,
:vip_promoter,
:arriving_with_client,
:client_alone,
:people,
:males,
:females,
:table_minimum,
:arrival_time,
:comments,
table_locations: [:id]
)
end
end
app/views/requests/_form.html.erb
...
<% TableLocation.all.each do |t| %>
<%= check_box_tag "request[table_locations][]", t.id, @request.table_locations.include?(t.id) %>
<%= t.location %>
<br />
<% end %>
...
解释:
您的 request_params
允许 table_locations: [:id]
,但这只允许以下格式:
Parameters: {"utf8"=>"✓", ... "request"=>{"table_locations"=>{"id"=>"1"}, "comments"=>""}, "commit"=>"Submit"}
但你的显示为:
Parameters: {"utf8"=>"✓", ... "request"=>{"table_locations"=>["1"], "comments"=>""}, "commit"=>"Submit"}
- 因此,试试这个:
puts request_params
inside the create method,你会注意到它没有 table_locations 值(即使你认为它在那里,但实际上没有),因为它没有 "properly" 在您的强参数 request_params
. 中列入白名单
为了能够将多个 TableLocations
对象关联到新建的 Request
对象,格式应如下所示
request = Request.new(table_location_ids: [1,2,3,4,5])
但是从你的实施来看,你是这样做的(这是行不通的):
request = Request.new(table_locations: [1,2,3,4,5])
# => this will raise an error:
# ActiveRecord::AssociationTypeMismatch: TableLocation expected, got Fixnum
# however yours didn't raise an error, because it was not whitelisted in the request_params in the first place
解决方案:
requests_controller.rb
def request_params
params.require(:request).permit(..., table_location_ids: [])
end
_form.html.erb
<% TableLocation.all.each do |t| %>
<%= check_box_tag "request[table_location_ids][]", t.id %>
<%= t.location %>
<br />
<% end %>
推荐:
以防万一您还不知道可以通过这种方式执行以下操作,我会将您的代码重构为如下所示:
requests_controller.rb
def create
@table_locations = TableLocation.all
end
_form.html.erb
<%= form_for @request do |f| %>
<% @table_locations.each do |table_location| %>
<%= f.check_box :table_location_ids, multiple: true, table_location.id %>
<% end %>
<% end %>
我有两个模型 Request 和 TableLocation 都具有 has_many 到 由 RequestLocation table.
加入的关系我正在尝试创建嵌套表单,但 table_location 数据未保存到数据库中。
如您所见,table_locations"=>["1"] 参数正在传递给创建操作但未保存。
感谢任何帮助。
控制台输出
Started POST "/requests" for 127.0.0.1 at 2017-06-07 10:35:26 -0400
Processing by RequestsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"mHV/xbmdfHmCAsi16KXlW+0bWVSkEo9SRVchdyPpL60o3m3SuKEt4nuUT4PJNEyCsWq3Nj4IWiCMlDbhiPewdA==", "request"=>{"concierge_name"=>"Alex", "concierge_number"=>"954-123-4567", "concierge_email"=>"alex@email.com", "client_name"=>"Adam", "client_number"=>"954-765-4321", "client_email"=>"adam@email.com", "hotel_employee"=>"0", "concierge_service"=>"0", "vip_promoter"=>"0", "arriving_with_client"=>"1", "client_alone"=>"0", "males"=>"", "females"=>"1", "table_minimum"=>"1000", "arrival_time(1i)"=>"2017", "arrival_time(2i)"=>"6", "arrival_time(3i)"=>"7", "arrival_time(4i)"=>"14", "arrival_time(5i)"=>"35", "table_locations"=>["1"], "comments"=>""}, "commit"=>"Submit"}
(0.1ms) BEGIN
SQL (0.4ms) INSERT INTO "requests" ("concierge_name", "concierge_number", "concierge_email", "client_name", "client_number", "client_email", "arriving_with_client", "people", "females", "table_minimum", "arrival_time", "comments", "created_at", "updated_at") VALUES (, , , , , , , , , , , , , ) RETURNING "id" [["concierge_name", "Alex"], ["concierge_number", "954-123-4567"], ["concierge_email", "alex@email.com"], ["client_name", "Adam"], ["client_number", "954-765-4321"], ["client_email", "adam@email.com"], ["arriving_with_client", "t"], ["people", 1], ["females", 1], ["table_minimum", 1000], ["arrival_time", "2017-06-07 14:35:00"], ["comments", ""], ["created_at", "2017-06-07 14:35:26.658718"], ["updated_at", "2017-06-07 14:35:26.658718"]]
(0.3ms) COMMIT
Redirected to http://localhost:3000/thanks
Completed 302 Found in 8ms (ActiveRecord: 0.8ms)
app/models/request.rb
class Request < ApplicationRecord
has_many :request_locations
has_many :table_locations, through: :request_locations
end
app/models/table_locations.rb
class TableLocation < ApplicationRecord
has_many :request_locations
has_many :requests, through: :request_locations
end
app/models/request_location.rb
class RequestLocation < ApplicationRecord
belongs_to :request
belongs_to :table_location
end
app/controllers/requests_controller.rb
class RequestsController < ApplicationController
before_action :set_request, only: [:show,
:edit,
:update,
:destroy]
before_action :authenticate_admin!, except: [:index,
:new,
:create]
def index
redirect_to root_path unless admin_signed_in?
@requests = Request.search(params[:term], params[:filter], params[:page])
end
def show
end
def new
@request = Request.new
end
def edit
end
def create
@request = Request.new(request_params)
@request.people = (@request.males || 0) + (@request.females || 0)
respond_to do |format|
if @request.save
format.html { redirect_to thanks_path, notice: 'Request was successfully created.' }
format.json { render :show, status: :created, location: @request }
else
format.html { render :new }
format.json { render json: @request.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if @request.update(request_params)
format.html { redirect_to @request, notice: 'Request was successfully updated.' }
format.json { render :show, status: :ok, location: @request }
else
format.html { render :edit }
format.json { render json: @request.errors, status: :unprocessable_entity }
end
end
end
def destroy
@request.destroy
respond_to do |format|
format.html { redirect_to requests_url, notice: 'Request was successfully destroyed.' }
format.json { head :no_content }
end
end
private
def set_request
@request = Request.find(params[:id])
end
def request_params
params.require(:request).permit(:concierge_name,
:concierge_number,
:concierge_email,
:client_name,
:client_number,
:client_email,
:hotel_employee,
:concierge_service,
:vip_promoter,
:arriving_with_client,
:client_alone,
:people,
:males,
:females,
:table_minimum,
:arrival_time,
:comments,
table_locations: [:id]
)
end
end
app/views/requests/_form.html.erb
...
<% TableLocation.all.each do |t| %>
<%= check_box_tag "request[table_locations][]", t.id, @request.table_locations.include?(t.id) %>
<%= t.location %>
<br />
<% end %>
...
解释:
您的
request_params
允许table_locations: [:id]
,但这只允许以下格式:Parameters: {"utf8"=>"✓", ... "request"=>{"table_locations"=>{"id"=>"1"}, "comments"=>""}, "commit"=>"Submit"}
但你的显示为:
Parameters: {"utf8"=>"✓", ... "request"=>{"table_locations"=>["1"], "comments"=>""}, "commit"=>"Submit"}
- 因此,试试这个:
puts request_params
inside the create method,你会注意到它没有 table_locations 值(即使你认为它在那里,但实际上没有),因为它没有 "properly" 在您的强参数request_params
. 中列入白名单
- 因此,试试这个:
为了能够将多个
TableLocations
对象关联到新建的Request
对象,格式应如下所示request = Request.new(table_location_ids: [1,2,3,4,5])
但是从你的实施来看,你是这样做的(这是行不通的):
request = Request.new(table_locations: [1,2,3,4,5]) # => this will raise an error: # ActiveRecord::AssociationTypeMismatch: TableLocation expected, got Fixnum # however yours didn't raise an error, because it was not whitelisted in the request_params in the first place
解决方案:
requests_controller.rb
def request_params
params.require(:request).permit(..., table_location_ids: [])
end
_form.html.erb
<% TableLocation.all.each do |t| %>
<%= check_box_tag "request[table_location_ids][]", t.id %>
<%= t.location %>
<br />
<% end %>
推荐:
以防万一您还不知道可以通过这种方式执行以下操作,我会将您的代码重构为如下所示:
requests_controller.rb
def create @table_locations = TableLocation.all end
_form.html.erb
<%= form_for @request do |f| %> <% @table_locations.each do |table_location| %> <%= f.check_box :table_location_ids, multiple: true, table_location.id %> <% end %> <% end %>