侧边栏壁纸
博主头像
极客日记 博主等级

行动起来,活在当下

  • 累计撰写 93 篇文章
  • 累计创建 17 个标签
  • 累计收到 1 条评论

目 录CONTENT

文章目录

【Ruby on Rails】Form 表单对象

Jack.Jia
2022-04-01 / 0 评论 / 0 点赞 / 7 阅读 / 0 字

form 与 表单对象

关于提到的各种 helper, 多看官方文档:

英文: http://guides.rubyonrails.org/form_helpers.html

中文: http://guides.ruby-china.org/form_helpers.html

也可以看 API 中的文档(写的也特别细) http://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#method-i-form_for

一个 form 表单有很多个参数怎么办?

对象的属性越多,传递过来的参数就越多,上面的赋值语句就越多。

我曾经见过 有 20 行语句, 都是: request.getParameter('…')

所以,解决方法: 使用表单对象 form object。来对这么多个参数进行封装.

使用 ORM 后, 做数据库操作时, 有三个层次要参与:

  • form object
  • ORM model
  • 数据库 table

例子:
一个 article 表单,有两个属性: title, content

  1. 纯 html

rails 是可以自动分辨, 某个参数的值,是: 字符串,数组, 还是 hash

<form action='..' method='..'>
   <input type='text' name='article[title]'/>        params[:article][:title]
   <input type='content' name='article[content]'/>   params[:article][:content]
</form>
  1. form object:
    在 rails 中是隐形的。你看不到它的声明。因为:它是在运行时,被 rails 中的某些方法动态创建的。

    动态创建方法的例子(javascript)

    my_string = 'function hi(){  console.info("hi") }'
    "function hi(){  console.info("hi") }"
    eval(my_string)
    undefined
    hi()  # =
    

    ( 在其他语言和框架中,这个对象,都是 显式 声明的(你的手写出来), 在 struts 中就要这样)

rails 中, 动态创建的 form object, 理论上是这样: (form 中包含什么参数, 或者说数据库中有多少列, 就有多少 attribute)

class Article
  attr_accessor :title
  attr_accessor :content
end

通过 form_for 来使用表单对象.

所以,rails 中, form 就要这样写:

<% form_for @aritcle do |f| %>
  <%= f.text_field :title %>
  <%= f.text_field :content %>
<% end %>

上面的重点,在于:do ... end. 它说明了 rails 是如何调用 上面的隐形的表单对象的。

这一句:

<%= f.text_field :title %>

就是调用了 上面的 Article 的 attr_accessor :title.

如果说 article 是个表单对象的话,

  • 编辑 article 的时候, 要显示原来的值, 就是:article.get_title
  • 保存 article 的时候, 要保存传过来的值,就是: article.title=

在上面的 do |f| ... end 中, 这个 f 就是表单对象.

f.text_field :title 不但会生成一个 <input type='text' /> 标签, 而且还会通过调用表单对象的 .get_title 方法, 来为这个 <input/> 标签设置初始值.

  1. ORM model.

app/models 目录下的 article.rb

class Article < ActiveRecord::Base
  # rails会自动生成:  title, content 的 accessor
end

( 也可以认为, 在 Rails 中, ORM model 跟 form object model 是一个文件。

  1. DB table:

articles 表:

  • 第一个列:title
  • 第二个列:content

表单对象与持久层, 在 Rails 中是一个.

  • 表单对象(处理表单代码时, 把参数保存到 对象中)
  • 持久层(把对象中的数据保存到 DB)

在 rails 中这两个是一样的东西。

使用 Hash 为 Model 赋值.

Rails 形式: 它的宗旨就是 方便程序员, 对人友好:

  1. 一个属性一个属性的赋值
article = Article.new({
    :title => params['article']['title'],
    :content => params['article']['content']
})
article.save  # 在这里执行  insert into articles values (...)
  1. 直接给构造函数一个 hash , 再 save
article = Article.new params['article']
article.save
  1. 把 new … save 的步骤, 省略成: create
    Article.create params['article']
    

form_for

下面是一个最常见的 Rails 表单:

<%= form_for @user do |f| %>
  <%= f.label :name %>
  <%= f.text_field :name %>
  <%= f.submit %>
<% end %>

form_for 是个方法。 要三个参数:

form_for(record, options = {}, &block)

对于下面的代码:

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

record@user, options, 就是 {}, 所以被省略掉了。 最后一个参数,是 block.

为什么,第二个参数可以被省略掉?

因为, 在 ruby 当中, 规定: 如果一个函数的参数中,有 block, 那么这个 block, 必须是最后一个参数。

所以,ruby ,要解析一个函数的时候,一开始,就能知道, do ... end 之间,这是一个 block, 所以,它就是最后的参数。

而:form_for @user, 表示,form_for 的第一个参数,就是 @user.

所以,根据 form_for 的定义:

  • 第一个参数,有了。
  • 第二个参数,在定义中,就是: 可选的。 (options = {} )
  • 第三个参数:(&block) 也有了。

所以这个函数就完整了,可以正常运行了。

下面的 f.label, f.text_field, f.submit, 中的 ``f:

<%= form_for @user do |f| %>
  <%= f.label :name %>
  <%= f.text_field :name %>
  <%= f.submit %>
<% end %>

这个 f, 就是 block 中的参数,也可以直接把它看成: form object. (表单对象)

为什么要用 rails 的自定义 html 标签呢?

例如:

form_for,
form_tag,
text_field,
select_tag

而不是:

<form>  , <input> ...

答: 为了更加简单

例如, 比较下面两种写法:

  1. form 的 rails 写法:
<%= form_for @post, html: { class: "edit_post", id: "edit_post_45" } do |f| %>
  <%= f.text :title, '我是标题' %>
<% end %>
  1. form 的 HTML 写法:
<form action='/posts'  method='post' class='edit_post' id='edit_post_45' >
  <input type='text' name='post[title]' value= '我是标题' />
</form>

我们发现两者差别不大.

但是, 在做 编辑某个记录的时候, 我需要:

  • 生成一个 form object
  • 生成的 html 标签, 带有默认值.
<%= form_for @post do |f| %>
  <%= f.text_field :title %>
<% end %>

例如:某个下拉框,有 100 个选项. 就要搞 100 次循环。然后,还要判断默认值。 那个时候,就会觉得代码特别的臃肿。

form_for 很智能的地方

过程: rails 发现了 form_for 的唯一参数: @post, 它就会开始按照 " 约定” 来猜测 各种参数: form_object, method, action …

所以, 对于 新建操作的 form:

<form action='/articles' method = 'post' >
</form>

和对于编辑操作的 form:

<form action='/articles/3/edit' method = 'post' >
  <input type='hidden' name='_method' value='put'/>
</form>

变成 ruby 代码的话就是:

<% if @article.id.present? %>
  <form action='/articles/3/edit' method = 'put' >
<% else %>
  <form action='/articles' method = 'post' >
<% end %>

于是, 我们就可以用:

<%= form_for @post %>

这段代码, 来表示上面提到的两种场合 (@post 是已经存在的记录, 还是没有存在的记录).

form 中的 authentity_token

每一个 <form> 中, 都有一个 authentity_token. 防止注入攻击 (XSS).

比如说, 我在购物时, 需要提交表单, 付费 . 这个表单当中, 很可能就是:

<form ... >
  <input name='price' value='1000'/>
</form>

这个 form 是非常好伪造的. 所以, 需要用 authentity_token 来识别.

<form ... >
  <input type='hidden' name='authentity_token' value='a1b2c3d4.....' />
  <input name='price' value='1000'/>
</form>

authentity_token 是由服务器端 (rails) 生成的. 它针对不同的 browser/session 生成不同的 token . 于是我们就可以在服务器端做验证了.

class PostsController ...
  protec_from_forgery  # 这行代码的作用: 对每个form 做验证,看里面的token 是否跟服务器端匹配.
end
```ruby
<% if @post.id.present? %>
  <form action='/posts/3/edit' method = 'put' >
<% else %>
  <form action='/posts' method = 'post' >
<% end %>
    <input type='hidden' name='token' value='<%= generate_token %>' />
    ...

所以使用 form tag , 就再也不用人肉写上面那行的代码了。

<input type='hidden' name='token' value='<%= generate_token %>' />

如果使用了 form tag, 就可以自动生成 csrf token 了:

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

使用 form object 生成输入项的默认值.

text_field, select 如何保证在编辑某个属性的时候, 它能选中了某个默认值?

<select>
  <option name=...> value</option>
  <option name=... <%=if @post.title == 'xx'  %> selected <% end %>> value</option>
  <option name=... selected> value</option>
  <option name=...> value</option>
</select>

在 rails 中, 可以使用 select 的辅助方法.

<%= select_tag options_for_select([1,2,3], default_value ) %>

FormHelper 和 FormTagHelper

两个例子,来对比说明。

区别:

  1. form helper:
 <%= f.text_field :title %>
  • 需要与 form object ( <%= form_for @book do |f| %> .. .<% end %> ) 配合使用。
  • 对于生成的 <input type='text' name="student[age]"/> 中, 它的 name 是自动生成的。 例如:name="student[age]",student 必须是某个 model 的实例 并且, age 必须是 form object 的方法(也就是 数据库的列。)

优点: 可以简化我们对表单项的操作。(例如: 下拉单 或 文本框的 默认值)

  1. form tag helper:
 <%= text_field_tag 'my_title' %>
  • 可以独立使用。 跟表单对象无关。
  • 名字可以随意取。 name='abc' 优点: 特别灵活。 可以脱离表单对象使用。 而且 便于理解。

相同点: 作用是一样的。

例子 1:

<%= f.submit "OK"%>
  <%= submit_tag "OK" %>

都会生成:

<input type="submit" name="commit" value="OK">

例子 2:

 <%= f.text_field :title %>
  <%= text_field_tag 'article[title]' %>

都会生成:

<input type="text" name="article[title]" id="article_title">

所以说, form helperform tag helper 都是一样的。 一个东西,写法不同。

甚至, 我们在 API 文档上,都可以看到 Rails 作者,告诉我们:

form helper: http://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html

form tag helper: http://api.rubyonrails.org/classes/ActionView/Helpers/FormTagHelper.html

都可以看到, xx_field 的文档中,会说: 请参考 xx_field_tag . 例如:

time_field 中:

Options: Accepts same options as time_field_tag

form_for 与 form_tag 的区别和联系

controller:

@book = Book.new

view 中, 下面两个是相同的:

<%= form_for @book do |f|%>
  <%= f.text_field :title %>
<% end %>
```ruby
<%= form_tag '/books' do %>
  <%= text_field_tag 'book[title]', '' %>
<% end %>

controller:

@book = Book.find(3)

view 中, 下面两个是相同的:

<%= form_for @book do |f|%>
  <%= f.text_field :title %>
<% end %>
```ruby
<%= form_tag '/books/3/update' do %>
  <%= text_field_tag 'book[title]', @book.title %>
<% end %>
0

评论区