iteye_16168 2009-06-29 15:52
浏览 271
已采纳

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条回答 默认 最新

  • horace_lee 2009-06-29 17:21
    关注

    你是对的
    我推断就是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 方法。

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥15 如何实验stm32主通道和互补通道独立输出
  • ¥30 这是哪个作者做的宝宝起名网站
  • ¥60 版本过低apk如何修改可以兼容新的安卓系统
  • ¥25 由IPR导致的DRIVER_POWER_STATE_FAILURE蓝屏
  • ¥50 有数据,怎么建立模型求影响全要素生产率的因素
  • ¥50 有数据,怎么用matlab求全要素生产率
  • ¥15 TI的insta-spin例程
  • ¥15 完成下列问题完成下列问题
  • ¥15 C#算法问题, 不知道怎么处理这个数据的转换
  • ¥15 YoloV5 第三方库的版本对照问题