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

Jam*_*mes 42 form-for ruby-on-rails-3

我在这里使用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.
  • 我可以做这个人做的事情并将表格分成多个文件.它看起来是迄今为止最好的选择,但我并不是很喜欢它,因为你不能只打开_form.html.erb看看发生了什么.

这些是我唯一的选择吗?

示例如下:

配置/ routes.rb中

Billing::Application.routes.draw do
  resources :customers, :shallow => true do
    resources :notes
  end
end
Run Code Online (Sandbox Code Playgroud)

耙路线| 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
Run Code Online (Sandbox Code Playgroud)

应用程序/视图/笔记/ _form.html.erb

#                      v----------------------------- Right here
<%= simple_form_for (@note), html: { class: 'form-vertical'} do |f| %>
  <%= f.input :content %>

  <%= f.button :submit %>
<% end -%>
Run Code Online (Sandbox Code Playgroud)

应用程序/视图/笔记/ new.html.erb

<h1>New note</h1>

<%= render 'form' %>

<%= link_to 'Back', customer_path(@customer) %>
Run Code Online (Sandbox Code Playgroud)

应用程序/视图/笔记/ edit.html.erb

<h1>Editing note</h1>

<%= render 'form' %>

<%= link_to 'Show', @note %>
<%= link_to 'Back', customer_path(@customer) %>
Run Code Online (Sandbox Code Playgroud)

应用程序/控制器/ 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
Run Code Online (Sandbox Code Playgroud)

Eri*_*ehs 34

如果您传递表单构建器的数组中的第一个对象是nil,则Rails将仅POST到第二个对象.因此,只需在控制器的编辑操作中设置 @customer 对象即可.如果您需要访问客户对象,请将其调用@note.

如果您使用相同的部分进行新建和编辑,则需要设置@note.customer控制器的新操作(@customer编辑时不会设置).

我认为这就是Rails团队的工作方式.


imd*_*rek 23

我想对詹姆斯的解决方案略作修改:

# app/helpers/application_helper.rb
def shallow_args(parent, child)
  child.try(:new_record?) ? [parent, child] : child
end
Run Code Online (Sandbox Code Playgroud)

而不是依赖于被称为"新"的控制器动作 - 尽管可能是95%的时间 - 这只是检查孩子是否是新记录.


Jam*_*mes 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
Run Code Online (Sandbox Code Playgroud)

应用程序/视图/笔记/ _form.html.erb

<%= simple_form_for shallow_args(@customer, @note), html: { class: 'form-vertical'} do |f| %>
  <%= f.input :content %>

  <%= f.button :submit %>
<% end -%>
Run Code Online (Sandbox Code Playgroud)

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

  • 这看起来像是一个不错的解决方案,但我真的希望Rails能够处理这个问题.毕竟,任何使用浅路线的人一旦创建他们的第一张表格肯定会遇到这个问题,对吗? (4认同)