RoR: form_for :symbol vs @object 做奇怪的事

RoR: form_for :symbol vs @object do the odd way

我正在学习 Getting Started with Rails 教程。
当为 create new article controller 生成 view 时,我使用:
案例 1:article

<%= form_for :article do |f| %>


并得到错误 No route matches [POST] "/articles/new"
我以为应该是 [POST] "/articles
案例2我改为:@artical

<%= form_for @article do |f| %>


没关系。

请注意,在 case 1 中,submit button 的文本是:Save article,在 case 2[ 中是 Create article =51=]
它与 This SOF answer.
相反 对我来说太暧昧了!!!那么有人可以帮我解释一下吗?
这是我的 source code on github

form_for :article 创建了一个表单,但默认 url 提交是 /articles/new

用于显示提交表单的文章操作 new,不处理创建新文章

=> resources :articles 将使用 GET 方法映射它

您可以为该表单指定一个 url,或者更改请求方法(不推荐)

<%= form_for :article, url: articles_path do |f| %>

简答

这就是 Rails 的工作方式,所以基本上,您应该将一个对象传递给 form_for

长答案

查看源代码后,这里发生了什么。 当您将对象传递给 form_for 时,action 将计算为:

url_for(polymorphic_path(@object))

如果对象是一篇未持久化的文章,它将给出 /articles,如果对象是一篇持久化的文章,它将给出 /articles/:id

当您传递字符串或符号时,action 会导致 url_for 调用 使用空散列,导致当前路径,在您的情况下 /articles/new。请注意,如果您显示来自 /articles/custom 的表单,则路径将为 /articles/custom.

对于按钮,如果传递一个对象,提交输入值将是 I18n.t('helpers.submit.create')I18n.t('helpers.submit.update') 取决于您的模型是否保留。但是,如果您传递字符串或符号,则该值将为 I18n.t('helpers.submit.submit').

这是来自 Rails 来源的相关代码行。

# actionview/lib/action_view/helpers/form_helper.rb#L422
def form_for(record, options = {}, &block)
    # .....
    when String, Symbol
      object_name = record
      object      = nil
    else
      object      = record.is_a?(Array) ? record.last : record
      raise ArgumentError, "First argument in form cannot contain nil or be empty" unless object
      object_name = options[:as] || model_name_from_record_or_class(object).param_key
      apply_form_for_options!(record, object, options)
    end
    # .....
    html_options = html_options_for_form(options[:url] || {}, html_options)
    form_tag_with_body(html_options, output)
  end

# actionview/lib/action_view/helpers/form_helper.rb#L451
def apply_form_for_options!(record, object, options) #:nodoc:
    # ....
    options[:url] ||= if options.key?(:format)
                        polymorphic_path(record, format: options.delete(:format))
                      else
                        polymorphic_path(record, {})
                      end
  end

# actionview/lib/action_view/helpers/form_tag_helper.rb#L840
def html_options_for_form(url_for_options, options)
  options.stringify_keys.tap do |html_options|
    # ....
    html_options["action"]  = url_for(url_for_options)
    # ....
  end
end

# actionview/lib/action_view/helpers/form_helper.rb#L1873
def submit_default_value
  object = convert_to_model(@object)
  key    = object ? (object.persisted? ? :update : :create) : :submit
  # .....

  defaults = []
  defaults << :"helpers.submit.#{object_name}.#{key}"
  defaults << :"helpers.submit.#{key}"
  defaults << "#{key.to_s.humanize} #{model}"

  I18n.t(defaults.shift, model: model, default: defaults)
end

# locale/en.yml#L142
helpers:
  select:
    prompt: Please select
  submit:
    create: Create %{model}
    submit: Save %{model}
    update: Update %{model}

对于操作,您可以看到在传递字符串时未调用 apply_form_for_options!,因此 options[:url] 仍然是 nil。传递对象时,如果未设置,apply_form_for_options!options[:url] 设置为 polymorphic_path(@object)。然后将其传递给 html_options_for_form,其中 action 设置为将 url_for 应用于值。

对于提交值,可以看到根据表单目标是否为对象取key,然后翻译。