How rails nested model form works(上)

在寫網站的過程當中,時常會用到nested model form。而因爲nested model form牽扯的東西比較多,理解起來並不容易。我打算從最簡單的form講起,介紹一下nested model form是如何工做的,以及一些常見的問題。html

經過form更新實例的流程大致是這樣的,咱們使用FormHelper來構建form,Rails會將form內的數據以'hash'的形式放入parmas;參數(params)被permit後,經過方法調用的方式,賦值給某個實例。ruby

我將從後向前解釋這個流程post

賦值 -- assign_attribute

params被permit後,咱們通常會調用assign_attribute,對實例進行賦值。
assign_attributes最終調用了_assign_attribute,網站

只考慮最簡單的狀況的話,_assign_attribute的代碼將會是這樣的:ui

def _assign_attribute(k, v)
  public_send("#{k}=", v)
end

能夠被這樣調用_assign_attribute(:name, "2dian718")code

這個方法是經過k(key)找到對應的setter方法,並傳入v(value)。orm

permit

assign_attributes內調用了sanitize_for_mass_assignment(給大量賦值消消毒),其做用是檢查傳入的參數是否有被permit。
若是沒有,則拋出異常ActiveModel::ForbiddenAttributesErrorhtm

ActionController::Parameters在初始化的時候,params的@permitted被設置爲false。因此咱們想用params進行賦值的時候,必須先要對參數進行permit。對象

咱們能夠用ActionController::Parameters.permit_all_parameters = true來改變這個默認行爲。token

form_for

<%= form_for(@company) do |f| %>
  <%= f.text_field :name %>
  <%= f.submit %>
<% end %>

將會生成

<form class="new_company" id="new_company" action="/companies" accept-charset="UTF-8" method="post"><input name="utf8" type="hidden" value="✓"><input type="hidden" name="authenticity_token" value="NeSXiKXJcMAyFcGQWfTDGE/7q21QWGjQoYN7IABUIFlnFkAtHFiv9PAjDaUOB0lu0pq7pDRj0JHsz+0jLJGDdw==">
  <input type="text" name="company[name]" id="company_name">
  <input type="submit" name="commit" value="Create Company">
</form>

咱們看到 <%= f.text_field :name %>被解釋成了<input type="text" name="company[name]" id="company_name">

關於f -- form builder

請注意,form_for的do後面跟了一個f
關於這個 f , rails的文檔裏是這樣解釋的,form_for會吐出來(yield)一個form builder的對象(就是那個f),他既瞭解form,也瞭解model(會在下文進行詳細介紹)。

咱們也能夠定製一個form builder,好比說給input自動加上標籤,代碼以下。

class LabellingFormBuilder < ActionView::Helpers::FormBuilder
  (field_helpers - [:label, :check_box, :radio_button, :fields_for, :hidden_field, :file_field]).each do |selector|
    class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
          def #{selector}(method, options = {})  # def text_field(method, options = {})
            @template.send("label", @object_name, method) +
             @template.send( #{selector.inspect}, @object_name, method, objectify_options(options))
          end
        RUBY_EVAL
  end
end

咱們能夠這樣用

<%= form_for(@company, builder: LabellingFormBuilder) do |f| %>
  <%= f.text_field :name %>
  <%= f.submit %>
<% end %>

將會生成

<form class="new_company" id="new_company" action="/companies" accept-charset="UTF-8" method="post"><input name="utf8" type="hidden" value="✓"><input type="hidden" name="authenticity_token" value="EeUl+Px6CvNhgs1nNucJfFiIYEc2zbsNDLzTwfxXcVxDF/JdRevVx6O0AVJhFIMKxelwjlL2A0xB8EXC0JLScg==">
  <label for="company_name">Name</label><input type="text" name="company[name]" id="company_name">
  <input type="submit" name="commit" value="Create Company">
</form>
相關文章
相關標籤/搜索