使用浅路由时,不同的路由需要不同的form_for参数

时间:2012-03-19 15:06:25

标签: ruby-on-rails-3 form-for

我在这里使用的是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])。如果接收到错误的参数,我将得到一个找不到方法的错误。

处理此问题的最佳方法是什么?

  • 我可以制作两种不同的形式,但这看起来很混乱。
  • 我必须为反向链接设置@customer,但我可以在表单中使用不同的变量(比如@customer_form),而不是在编辑和更新方法中设置它,但这是不一致的,有点混乱,因为我必须在新方法中设置@customer_form和@customer。
  • 我可以做this guy所做的事情,并将表单拆分为多个文件。它看起来是迄今为止最好的选择,但我不太喜欢它,因为你不能只打开_form.html.erb并看看发生了什么。

这些是我唯一的选择吗?

示例如下:

配置/ 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

3 个答案:

答案 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 -%>

我不知道这是最好的解决方案,但似乎工作正常。