我在这里使用的是Simple Form,但这也是普通Rails表单的一个问题。使用浅路由时,form_for需要不同的参数,具体取决于它使用的上下文。
示例:要进行编辑(http://localhost:3000/notes/2/edit
),_ form.html.erb需要simple_form_for(@note)
。但是要创建新笔记(http://localhost:3000/customers/2/notes/new
),_ form.html.erb需要simple_form_for([@customer, @note])
。如果接收到错误的参数,我将得到一个找不到方法的错误。
处理此问题的最佳方法是什么?
这些是我唯一的选择吗?
示例如下:
配置/ routes.rb中
Billing::Application.routes.draw do
resources :customers, :shallow => true do
resources :notes
end
end
耙路线| grep note
customer_notes GET /customers/:customer_id/notes(.:format) notes#index
POST /customers/:customer_id/notes(.:format) notes#create
new_customer_note GET /customers/:customer_id/notes/new(.:format) notes#new
edit_note GET /notes/:id/edit(.:format) notes#edit
note GET /notes/:id(.:format) notes#show
PUT /notes/:id(.:format) notes#update
DELETE /notes/:id(.:format) notes#destroy
应用/视图/笔记/ _form.html.erb
# v----------------------------- Right here
<%= simple_form_for (@note), html: { class: 'form-vertical'} do |f| %>
<%= f.input :content %>
<%= f.button :submit %>
<% end -%>
应用/视图/笔记/ new.html.erb
<h1>New note</h1>
<%= render 'form' %>
<%= link_to 'Back', customer_path(@customer) %>
应用/视图/笔记/ edit.html.erb
<h1>Editing note</h1>
<%= render 'form' %>
<%= link_to 'Show', @note %>
<%= link_to 'Back', customer_path(@customer) %>
应用/控制器/ notes_controller.rb
class NotesController < ApplicationController
def show
@note = Note.find(params[:id])
@customer = Customer.find(@note.customer_id)
respond_to do |format|
format.html
format.json {render json: @note }
end
end
# GET /notes/new
# GET /notes/new.json
def new
@note = Note.new
@customer = Customer.find(params[:customer_id])
respond_to do |format|
format.html # new.html.erb
format.json { render json: @note }
end
end
# GET /notes/1/edit
def edit
@note = Note.find(params[:id])
@customer = Customer.find(@note.customer_id)
end
# POST /notes
# POST /notes.json
def create
@customer = Customer.find(params[:customer_id])
@note = @customer.notes.build(params[:note])
respond_to do |format|
if @note.save
format.html { redirect_to @customer, notice: 'Note was successfully created.' }
format.json { render json: @note, status: :created, location: @note }
else
format.html { render action: "new" }
format.json { render json: @note.errors, status: :unprocessable_entity }
end
end
end
# PUT /notes/1
# PUT /notes/1.json
def update
@note = Note.find(params[:id])
@customer = Customer.find(@note.customer_id)
respond_to do |format|
if @note.update_attributes(params[:note])
format.html { redirect_to @customer, notice: 'Note was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: @note.errors, status: :unprocessable_entity }
end
end
end
# DELETE /notes/1
# DELETE /notes/1.json
def destroy
@note = Note.find(params[:id])
@note.destroy
respond_to do |format|
format.html { redirect_to :back }
format.json { head :no_content }
end
end
end
答案 0 :(得分:33)
如果您传递表单构建器的数组中的第一个对象是nil
,则Rails将仅POST到第二个对象。因此,只需不要在控制器的编辑操作中设置 @customer
对象。如果您需要访问客户对象,请通过@note
。
如果您使用相同的部分进行新建和编辑,则需要在控制器的新操作中设置@note.customer
(编辑时不会设置@customer
。)
我认为这就是Rails团队希望它发挥作用的方式。
答案 1 :(得分:22)
我想对詹姆斯的解决方案稍作修改:
# app/helpers/application_helper.rb
def shallow_args(parent, child)
child.try(:new_record?) ? [parent, child] : child
end
而不是依赖于被称为“新”的控制器动作 - 尽管它可能是95%的时间 - 这只是检查孩子是否是新记录。
答案 2 :(得分:9)
以下是我提出的建议:
应用/助手/ application_helper.rb 强>
module ApplicationHelper
# Public: Pick the correct arguments for form_for when shallow routes
# are used.
#
# parent - The Resource that has_* child
# child - The Resource that belongs_to parent.
def shallow_args(parent, child)
params[:action] == 'new' ? [parent, child] : child
end
end
应用/视图/笔记/ _form.html.erb 强>
<%= simple_form_for shallow_args(@customer, @note), html: { class: 'form-vertical'} do |f| %>
<%= f.input :content %>
<%= f.button :submit %>
<% end -%>
我不知道这是最好的解决方案,但似乎工作正常。