form_for中的sumbit如何判断是Create还是Update

form_for 中的 submit没有指定任何参数,rails可以自动的区别是 Create还是Update,这他妈太神奇了,new和edit的view的代码几乎一样。

初步认知:
rails是用的REST,
当create时用POST ,update时用put,我观察了html源码,发现edit传参时,有个hidden变量 _method值为put,现在问题似乎明白了。
但新问题出现,controller中的edit方法,为什么会传hidden变量过去呢?
[b]问题补充:[/b]
可能我的表达方式有点问题。
我的意思是,为什么调action中的edit方法时,他会在view里自动生成hidden变量。

昨天下午无奈之下查看了源代码,终于明了了。
file: rails-2.3.2/actionpack/lib/action_view/helpers/form_helper.rb
[code="ruby"]
def form_for(record_or_name_or_array, *args, &proc)
raise ArgumentError, "Missing block" unless block_given?

    options = args.extract_options!

    case record_or_name_or_array
    when String, Symbol
      object_name = record_or_name_or_array
    when Array
      object = record_or_name_or_array.last
      object_name = ActionController::RecordIdentifier.singular_class_name(object)
      apply_form_for_options!(record_or_name_or_array, options)
      args.unshift object
    else
      object = record_or_name_or_array
      object_name = ActionController::RecordIdentifier.singular_class_name(object)
      apply_form_for_options!([object], options)
      args.unshift object
    end

    concat(form_tag(options.delete(:url) || {}, options.delete(:html) || {}))
    fields_for(object_name, *(args << options), &proc)
    concat('</form>')
  end

  def apply_form_for_options!(object_or_array, options) #:nodoc:
    object = object_or_array.is_a?(Array) ? object_or_array.last : object_or_array

    html_options =
      if object.respond_to?(:new_record?) && object.new_record?
        { :class  => dom_class(object, :new),  :id => dom_id(object), :method => :post }
      else
        { :class  => dom_class(object, :edit), :id => dom_id(object, :edit), :method => :put }
      end

    options[:html] ||= {}
    options[:html].reverse_merge!(html_options)
    options[:url] ||= polymorphic_path(object_or_array)
  end

[/code]

可以看出当object.respond_to?(:new_record?) && object.new_record?条件不成立时,会将method置为put

2个回答

你是对的
我推断就是REST机制不是特别清楚而已

至于,你最好说的为什么,传递hidden变量

那表单不就是传值的吗,hidden值也是值啊

就说没有REST存在,你自己写个表单,提交到一个方法,然后表单有个hidden值,一样的传。

最好,再贴点REST相关材料,你看看,可能有帮助

接下来,这个问题就不是问题了,关键在于这个 FORM 的提交方式。REST 认为:
PUT 表示修改一个既存的资源
POST 表示创建一个新的资源
DELETE 自然表示删除
[img]http://mypages.iteye.com/upload/picture/pic/28467/1210821d-9e34-3c63-9016-7dba325191af.jpg[/img]
传统的link_to 所生成的链接中包含了controller和action,相对比,使用新的 “project_path” 所创建的链接,只包含了controller 和 资源的id –毫无疑问,这是一个 REST风格的URL。因为链接默认的是一个“Get”请求,Rails 能够知道这一点,所以就会去调用 show action。对于每一个资源,rails 都会有7个标准的 path 方法,这些可以从表1.2中看到。

每一个path 方法都关联一个 http 协议的动作,一些请求(例如show,create),可以通过http协议的 Get 或 Post 传递给服务器;但是有一些请求,如 update,delete,则需要一些其他的方式(如使用隐藏的变量)传递给服务器,因为浏览器并不知道 PUT和DELETE动作。接下来的章节我们会仔细的介绍。

进一步看看这个表,我们也会发现4个http 动作,并不足以包含全部的CRUD操作。前2个方法使用Get的方式会工作的很好,但是对于new_project_path 和 edit_project_path 就不同了。

New 和 Edit
用户如果点击一个“新建”链接,那么会使用Get动作来对服务器发送一个请求。下面的例子表明,生成的链接是由 controller 和一个“new”action 组成的。

[code="ruby"]link_to "New", new_project_path
=>
New[/code]

这是对REST思想的一种破坏?或许乍看之下确实如此。但是如果你仔细看,那么一切都会清晰,“new”并不是一个CURD的action,它更像一个建立一个新的资源之前的准备的动作。真正的CRUD 中的create被调用,是在新的form被提交的以后才执行的。这个链接当然也没有资源的id—因为资源还没有被创建。一个链接如果没有资源的id,那么就不应该被称为REST的URL,因为REST的URL总是会指定一个资源的id。所以,这个 “new” action 仅仅因该用来显示一个新的form的页面而已。

对于 edit_project_path ,也是同样的道理。它引用了一个资源,但是仅仅是在调用 update action 之前的准备工作。真正的update action 是在页面被提交以后才执行的。edit_project_path 和 new_project_path 唯一的区别就是前者需要使用一个资源的id。按照REST的规则,资源的id放在controller 的后面:/project/1 。但是如果仅仅使用Get 动作来提交这个URL,那么Rails将会认为你要调用的是show action。为了防止这一点,edit_project_path 方法扩展了一下生成的链接,例如:

[code="ruby"]link_to "Edit", edit_project_path(project)
=>
Edit[/code]

这样,我们就能理解为什么允许 edit_project_path 和 new_project_path生成的链接里带有 action 了,因为他们两个都不是REST的 CRUD URL,他们仅仅是准备工作。还有其它的一些URL和这两个很相似,我们会在后面的章节介绍。

在 form 中使用 path 方法:Create 和 Update
传统的方式上,我们使用 form_tag 或 form_for 来创建一个form:

[code="ruby"]<% form_for :project, @project, :url => { :action => "create" } do |f|
%>
...
<% end %>[/code]

在REST应用中,这个 :url hashmap 会被 path 方法给取代:

“project_path” 创建新的资源所使用的form
“project_path(:id)”编辑一个资源所使用的form

a) 创建资源所使用的form
form 使用 post 动作向服务器提交信息,“project_path”方法并不会有资源id作为参数,这样,生成的URL就应该是“/projects”这个样子。当提交到服务器以后,就会调用 create action。

[code="ruby"]form_for(:project, :url => projects_path) do |f| ...
=>

[/code]

b) 编辑一个资源所使用的form
按照REST的思想,一个更新的操作是使用http协议的PUT动作来发送的。但是,正如我们所知道的,浏览器只明白 Post和Get动作。解决的办法就是使用

form_for 方法里的 :html 参数。
[code="ruby"]form_for(:project, :url => project_path(@project),
:html => { :method => :put }) do |f| ...
=>

[/code]
Rails 生成了一个隐藏的字段来代替http的put 动作。提交以后,Rails 会检查这个变量,然后判断是否去调用update方法。

删除
恐怕我们已经发觉了,用于显示和删除一个资源,所使用的path方法都一样:

[code="html"]link_to "Show", project_path(project)
link_to "Destroy", project_path(project), :method => :delete[/code]

唯一的不同就是 删除的时候,使用了一个变量 :method,用它来表示http的DELETE动作。因为浏览器不支持DELETE动作,所以,Rails 会生成一些javascript来解决这个问题:

[code="js"]link_to "Destroy", project_path(project), :method => :delete
=>
onclick="var f = document.createElement(’form’);
f.style.display = ’none’; this.parentNode.appendChild(f);
f.method = ’POST’; f.action = this.href;
var m = document.createElement(’input’);
m.setAttribute(’type’, ’hidden’);
m.setAttribute(’name’, ’_method’);
m.setAttribute(’value’, ’delete’); f.appendChild(m);f.submit();
return false;">Destroy
[/code]

这段javascript 会生成一个form,把 http 的DELETE动作放在隐藏变量里传递给服务器,然后,Rails 会判断这个变量,决定是否去调用destroy 方法。

:D Anyway,
得到了你想要的就好 :idea:

Csdn user default icon
上传中...
上传图片
插入图片
抄袭、复制答案,以达到刷声望分或其他目的的行为,在CSDN问答是严格禁止的,一经发现立刻封号。是时候展现真正的技术了!
立即提问