Ruby on Rails 網站程式設計基礎班
第六課
表單與資料驗證

回憶一下上結課...
Rails MVC 架構的運作流程
ActiveRecord 的概念
關聯式資料庫的關聯 (一對一, 多對多)
ActiveRecord 的關聯 (has_many, belongs_to)
常用的 ActiveRecord 查詢法
# find 是透過資料的 id 搜尋
user = User.find(1) #尋找 users 資料表裡 id 為 1 的 record
# where 是透過給訂的條件搜尋
result = User.where(name: "bob") # 會回傳一個有一或多筆 user 的陣列
# ActiveRecord 會有內建的搜尋方法,find_by_
bob = User.find_by_name("bob")
# find_by_ 後面會接上相對應資料表裡的所有的欄位名稱,是由 ActiveRecord 動態產生的方法
# 問題是,若符合條件的資料不只一筆,find_by_ 只會回傳符合條件的第一筆資料,請小心使用
Validation (驗證)
確保輸入資料庫的資料的正確性 (空字串、格式 etc.)
需要讓資料被存入資料庫前檢查正確性
問題是應該在架構中的哪一層做驗證?
Validation (驗證)
Rails 是在 Model 的層面做資料的驗證
1. 好處是不需要考慮資料庫的種類 (把驗證寫在資料庫)
2. 無法在用戶端(瀏覽器)跳過驗證 (把驗證寫在前端)
3. 更容易測試與維護 (不需查看噁心的 SQL 或 javascript)
Validation (驗證)
# 我們要確保資料輸入的正確性
# 像是以 User 資料表為例,要是沒有 name 和 email,就不該被建立
class User < ApplicationRecord
validates :name, presence: true
validates :email, presence: true
end
Validation (驗證)
# 我們要確保輸入的名字有一定的長度
class User < ApplicationRecord
validates :name, presence: true
validates :name, length: { minimum: 2 }
validates :name, length: { maximum: 200 }
#名字最少要有兩個英文字母,最多不可超過兩百個英文字母
validates :email, presence: true
end
Validation (驗證)
# 我們要確保輸入的名字有一定的長度
class User < ApplicationRecord
validates :name, presence: true
validates :name, length: { minimum: 2 }
validates :name, length: { maximum: 200 }
validates :email, presence: true
validates :email, uniqueness: true
#任何 email 應該都是獨一無二的,所以應該卻確保它的獨特性
end
Validation (驗證)
表單 (Form)
今天的網頁服務或應用
要讓使用者建立或是編輯資料
一定需要某一種類型的表單
表單 (Form)
class OrdersController < ApplicationController
# GET /orders/new
def new
@order = Order.new
end
end
我們來回顧一下上星期的 Scaffold 程式碼...
Order.new 是在創造出一個 Order 的空物件
存入 @order 變數後,就能能被前端頁面的 /orders/new 的表單使用
表單 (Form)
<h1>New Order</h1>
<%= render 'form' %>
<%= link_to 'Back', orders_path %>
我們再看一下前端 new 頁面的程式碼...
表單的程式碼其實是被封裝到一個叫 _form.html.erb 的樣板裡,然後被
<% render 'form' %> 呼叫
表單 (Form)
<%= form_for(@order) do |f| %>
<% if @order.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(@order.errors.count, "error") %> prohibited this order from being saved:</h2>
<ul>
<% @order.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :name %><br>
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :phone %><br>
<%= f.text_field :phone %>
</div>
<div class="field">
<%= f.label :description %><br>
<%= f.text_area :description %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
實際表單的程式碼...
這邊注意,form_for 是 rails 一個內建的方法,需要綁定 ActiveRecord 的物件使用,而目前 @order 就是我們在 new action 裡宣告的 @order
<%= f.text_field :phone %> 是綁定 @order 物件的 phone 屬性,自動幫你轉成文字輸入匡的 html 程式碼
表單 (Form)
class OrdersController < ApplicationController
# POST /orders
def create
@order = Order.new(order_params)
if @order.save
redirect_to @order
else
render :new
end
end
private
def order_params
params.require(:order).permit(:name, :phone, :description)
end
end
接下來我們來看一下 create action 的程式碼...
從前端傳回來的資料都會被放入 params 參數(是一個 hash) 裡面,讓後端取用並實例出新的 @order 物件
為了防止有人從前端輸入惡意的資料或參數,Rails 會要求 params 裡的資料必須經過一個過濾的安全步驟,這個機制叫做 strong parameters
Strong Parameters
class OrdersController < ApplicationController
private
def order_params
params.require(:order).permit(:name, :phone, :description)
end
end
require 和 permit 會把 params hash 裡的:
params[:order][:name]
params[:order][:phone]
params[:order][:description]
過濾出來,只能讓這些資料被寫入 orders 資料庫
因為 orders 資料表的 schema 會改變,每次改變都意味著 orders_params 需要調整,因此我們把它寫成 private method
表單 (Form)
class OrdersController < ApplicationController
# POST /orders
def create
@order = Order.new(order_params)
if @order.save
redirect_to @order
else
render :new
end
end
private
def order_params
params.require(:order).permit(:name, :phone, :description)
end
end
接下來我們來看一下 create action 的程式碼...
.save 代表存入資料庫,若物件是新的,便會在資料庫產生新的資料,若該物件代表的資料已存在,便會更新該筆資料
若儲存成功,便會導回 show 頁面,若失敗,便會再次顯示 new 頁面 (也就是表單)
表單 (Form)
<h1>Editing Order</h1>
<%= render 'form' %>
<%= link_to 'Show', @order %> |
<%= link_to 'Back', orders_path %>
我們再看一下前端 edit 頁面的程式碼...
與 new 頁面一樣,表單的程式碼其實是被封裝到一個叫 _form.html.erb 的局部樣板(partial template) 裡,然後被
<% render 'form' %> 呼叫
View Partial (局部樣板)
把重複的前端程式碼獨立成一個單獨的檔案,來讓其他樣板共享引用,有點像是呼叫 method,因為 new 和 edit 頁面的表單程式碼相同,可以把表單的程式碼獨立出來
表單 (Form)
class OrdersController < ApplicationController
# GET /orders/1/edit
def edit
@order = Order.find(params[:id])
end
# PATCH/PUT /orders/1
def update
@order = Order.find(params[:id])
if @order.update(order_params)
redirect_to @order
else
render :edit
end
end
private
def order_params
params.require(:order).permit(:name, :phone, :description)
end
end
另外,我們也注意到 edit 和 update 有重複的程式碼...
before_action
class OrdersController < ApplicationController
before_action :set_order, only: [:edit, :update]
# GET /orders/1/edit
def edit
end
# PATCH/PUT /orders/1
def update
if @order.update(order_params)
redirect_to @order
else
render :edit
end
end
private
def set_order
@order = Order.find(params[:id])
end
def order_params
params.require(:order).permit(:name, :phone, :description)
end
end
before_action 是指會在該 action 執行前會觸發的方法,所以我們可以把尋找單筆資料的程式碼打包成一個 method: set_order,再設定 before_action 會在執行 edit 和 update 前呼叫 set_order
功課:留言板
1. 打造出留言板需要的資料表
2. 實作出 Post 的 List、New、Edit 與 Show 頁面
RoR 第六課:表單與驗證
By Eugene Chang
RoR 第六課:表單與驗證
- 1,213