因為項目變有點多了,請使用Ctrl+F搜尋標題,( )內是這篇大綱不用加入搜尋
1.Rails四大觀點 (命名規則,MVC)
2.在Cloud9下指令執行網頁 (用Scaffold支架)
3.DIY之1 (命名錯誤悲劇,瞭解Rails MVC流程,看懂錯誤訊息)
4.ERB (在網頁執行Ruby語法)
5.資料表關連 (一對多,多對多,hirb-unicode套件)
6.更改Schema.rb內容 (改欄名,改欄屬,增加欄位,刪除欄位)
7.快速美化頁面與表格分頁 (bootstrap-sass和Kaminari套件)
8.form形式的dropdownlist (單純的下拉式欄位和連資料關連的下拉式欄位)
9.限制輸入框
10.驗證格式 (其實Rails也有一套驗證規則)
11.強大Try
12.相對路徑的圖片
13.製作後台路徑
14.DIY之2 (寫出CRUD,before_action,model.find)
15.不同頁面傳值問題
16.datetime轉換格式
17.render載入
18.flash與session
19.指定layout版面
20.routes中as用處 (幫路徑取別名)
21.Helper
22.多國語系
23.寄Email (Gmail,Mandrill,Mandrill Template)
24.現在寄 VS 待會寄 (delayed_job套件)
25.SQL語句 (以前SQL語句+Rails偷懶寫法=少好幾行程式碼)
26.問題主鍵是Id (解決複合鍵與屬性String問題)
Rails四大觀點
CoC:慣例優於構造
先提一下CoC命名規則(後面DIY會示範命名錯誤產生悲劇)
1.resource :複數
2.Model檔名單數
3.db table名稱為複數
4.Controller檔名複數
5.雖然英開頭大小無差,但最好小寫
Rails命名規則參考網址:http://fireqqtw.logdown.com/posts/206952-rails-naming-conventions
DRY:不要重複程式
CRUD:增讀更改刪除
MVC:模組視窗控制
以下為MVC分工原則(對不起我畫的很糟...)
在Cloud9下指令執行網頁
首先在workspace路徑下創個Rails專案,並切到資料夾下
rails new 專案名稱
cd 專案名稱/
成功之後在左視窗會看到創好專案,接著來介紹專案底下默默地成員們
app資料夾:放M(模組)V(前端)C(後端)主要地方
config資料夾:放初始與環境值設定
db資料夾:sqllite資料庫與migrate(支架)
test資料夾:測試程式
Gemfile檔案:安裝套裝版本設定,關於>= 2.3.0是大於等於2.3.0版本就更新,~>2.3.0是2.3系列小版本更新但2.4以上大版本不更新
瞭解這些成員後,要開始建立支架(設定存在db/migrate/XXXX.rb)
rails generate scaffold 表格名稱 欄位1:欄位1屬性 欄位2:欄位2屬性...
關於支架有四點注意:
1.欄位屬性如果沒設定會默認String
2.不需創id:integer,Rails會自動生成
3.不需創created_at:date,Rails會自動生成
4.不需創updated_at:date,Rails會自動生成
因為支架只是想像(俗稱沒存檔),想放進資料倉庫就用這指令(設定存在db/schema.rb或db/development.sqllite3...其他資料庫)
rake db:migrate
最後執行Rails要開啟伺服器
rails server -p $PORT -b $IP
如果要中斷Ctrl+C
DIY之1(示範CoC命名規則重要性)
前面用Scaffold支架做出MVC,連CRUD都自動弄好,但是會造成你永遠搞不清楚Rails背後運作原理。
因此這回不用Scaffold,通通自己來做做看和除錯誤!
老慣例創個新專案並切到資料夾下(對不起個人英文單字很差)
rails new fishstore
cd fishstore/
接著來認識控制老大--"routes.rb" (config/routes.rb)
他是控管瀏覽器的網址列,例如預設首頁或某資料夾下某網頁
預設首頁範例
Rails.application.routes.draw do
root 'firstpage#index' #root 'controller name#def name'
end
格式為
root '控制名稱#方法名稱' <<小寫英文開頭
透過另一台終端機掛著rails server -p $PORT -b $IP,發生錯誤
uninitialized constant FirstpageController (就是找不到firstpage_controller.rb)
因為在app/controllers下是沒有這玩兒,於是我們需生出來
rails generate controller firstpage
然後再去執行看看又出錯
The action 'index' could not be found for FirstpageController (你說的index方法沒有寫啊)
雖然幫我們創C的檔案和V資料夾,可是Rails不會幫我們生方法,所以要寫程式了。(app/controllers/firstpage_controller.rb)
class FirstpageController < ApplicationController
def index
end
end
接著跑去執行,想當然又錯
Missing template firstpage/index, application/index with {:locale=>[:en], :formats=>[:html], :variants=>[], :handlers=>[:erb, :builder, :raw, :ruby, :coffee, :jbuilder]}. Searched in: * "/home/ubuntu/workspace/fishstore/app/views"
(你的Views/firstpage下沒有index網頁啊)
於是我們創個index.html.erb (Views/firstpage/index.html.erb)
注意創V網頁檔格式別忘能翻譯ruby的副檔名erb
XXX.html.erb
執行後終於沒紅字!不過這只有C和V聊天,於是我們再加上M來玩玩。
rails generate model Fish <<大寫英文開頭
接著在migrate發現新的支架rb檔(db/migrate/XXX_create_fish.rb)
class CreateFish < ActiveRecord::Migration
def change
create_table :fish do |t|
t.timestamps
end
end
end
新增一些欄位與屬性
t.string :fish_name
t.string :fish_type
t.integer:fish_price
t.boolean:is_onsale
如果這時去執行會有這錯誤
Migrations are pending. To resolve this issue, run: bin/rake db:migrate RAILS_ENV=development (沒有Schema.rb)
用rake db:migrate確定建表格就沒此紅字,我們可以常用rake db:migrate:status知道目前migrate的狀態
前面花大半時間生出MVC,可是沒生出CRUD,接下來就繼續努力
回到老大---routes.rb,增加http動作get指定新路徑
Rails.application.routes.draw do
root 'firstpage#index' #root 'controller name#def name'
get '/new', to: 'firstpage#new' #get 'path', to: 'controller name#def name'
end
格式為
get '路徑',to: '控制名#方法名'
關於路徑怎麼知道,可以用rake routes觀察到firstpage#new往前看要GET是/new,firstpage#index往前看要GET是/
Prefix Verb URI Pattern Controller#Action
root GET / firstpage#index
new GET /new(.:format) firstpage#new
接著views/firstpage下創個new.html.erb,並隨意打個內容
have flowers <<單純測試有跑到這頁
把index.html.erb增加連結
<a href="/new">add fish</a> <<適合外連
<%= link_to "add fish", new_path %> <<適合內連,格式為<%= link_to "連結名稱", 路徑_path %>
至於我們在routes有說有def new,當然要改firstpage_controller.rb
class FirstpageController < ApplicationController
def index
end
def new
end
end
最後來執行發現"沒問題",但是如果我們要CRUD,必須在routes.rb寫路徑共8次,真是不夠DRY!((喂
因此Rails增加新成員,用"resources:XXXs"方法自動獲得8個路徑
根據Rails慣例resources的名稱必須複數,於是又根據我爛英文取個fishs (正確是:fishes)
Rails.application.routes.draw do
resources :fishs #auto to have eight urls,path name have add "s" ex:fish"s" <<注意這裡因為英文單字錯誤導致後續連環錯
root 'firstpage#index' #root 'controller name#def name'
end
接著如果你去執行會發現錯誤
Showing /home/ubuntu/workspace/fishstore/app/views/firstpage/index.html.erb where line #2 raised:
undefined local variable or method `new_path' for #<#<Class:0x0000000497e600>:0x0000000497d228>
Extracted source (around line #2):
1 2 |
<a href="/new">add fish</a>
<%= link_to "add fish", new_path %>
|
喔,因為我們路徑進化了,用rake routes查
Prefix Verb URI Pattern Controller#Action
fishs GET /fishs(.:format) fishs#index
POST /fishs(.:format) fishs#create
new_fish GET /fishs/new(.:format) fishs#new
edit_fish GET /fishs/:id/edit(.:format) fishs#edit
fish GET /fishs/:id(.:format) fishs#show
PATCH /fishs/:id(.:format) fishs#update
PUT /fishs/:id(.:format) fishs#update
DELETE /fishs/:id(.:format) fishs#destroy
root GET / firstpage#index <<不是自動出8個路徑之一,而是一開始我們早設好的預設首頁
沒錯我們的new_path進化成new_fish_path,真是酷阿!不過別開心太早,因為點下連結後又出現紅字
uninitialized constant FishsController (沒這fishs_controller.rb)
由此可得知當resources一出,前面C啊V啊又要生出一遍
rails g controller fishs
在views/fishs/index,new....創新網頁檔
然後在這時我竟然有點錯亂,有兩個index到底是會怎麼繞?
以下是用Cloud9網頁說明網址差別,也證明routes是真老大!
https://rails-maro150cm.c9.io/ <<root 因此是firstpage家index.html.erb
https://rails-maro150cm.c9.io/fishs <<resources 因此是fishs家index.html.erb
好像只有我這白癡忘記看路徑
由於我們用resources把firstpage的new GET權利搶走(?),因此要出現需要用get
Rails.application.routes.draw do
resources :fishs #auto to have eight urls,path name have add "s" ex:fish"s"
root 'firstpage#index' #root 'controller name#def name'
get '/new', to: 'firstpage#new' #get 'path', to: 'controller name#def name'
end
搞好路徑後,開始寫一段新增
fishs_controller.rb
class FishsController < ApplicationController
def index
@fishs=Fish.all <<主頁顯示全部資料
end
def new
@fish=Fish.new <<新增一筆資料
end
def create
@fish=Fish.new(fish_params) <<新增時會傳入fish_params方法,可以防止駭客多傳參數
if @fish.save <<當@fish存入時
redirect_to fishs_path <<自動轉回index.html.erb
else
render :new <<給予new.html.erb
end
end
private <<避免被人知道參數有幾個而宣告private
def fish_params
params.require(:fish).permit(:fish_name,:fish_type,:fish_price) <<根據permit要求僅能傳入這三個參數
end
end
fishs/index.html.erb
<h1>fish list</h1>
<ul>
<% @fishs.each do |fish| %> <<把@fishs全部魚資料分別顯示出來
<li><%= fish %></li> <<顯示預存Prc
<li><%= fish.fish_name %></li> <<顯示魚姓名欄位值
<% end %>
</ul>
<a href="/fishs/new">add fish</a>
<%= link_to "add fish", new_fish_path %>
fishs/new.html,erb
<h2>fish list</h2>
<%= form_for(@fish) do |f| %> <<表單模式,資料與@fish傳過來形式對照
<%= f.label :fish_name,'Name' %>
<%= f.text_field :fish_name %>
<%= f.label :fish_type,'Type' %>
<%= f.text_field :fish_type %>
<%= f.label :fish_price,'Price' %>
<%= f.number_field :fish_price %>
<%= f.submit %>
<% end %>
<%= link_to 'Back', fishs_path %>
接著竟想不到執行出錯
Showing /home/ubuntu/workspace/fishstore/app/views/fishs/new.html.erb where line #2 raised:
undefined method `fish_index_path' for #<#<Class:0x007f92382e1e50>:0x007f923806d6d8>
Extracted source (around line #2):
1 2 3 4 5 |
<h2>fish list</h2>
<%= form_for(@fish) do |f| %>
<%= f.label :fish_name,'Name' %>
<%= f.text_field :fish_name %>
<%= f.label :fish_type,'Type' %>
|
說實話這段我卡超久,明明book就OK,跳到fish就不OK
上網找來找去發現Rails有"可數"慣例,簡單來說因為我model fish,自動生出table fish,但最主要是resource fishs導致@fish錯誤 (下次我會乖乖上Google查英文)
所以Rails命名超級重要,不然會狂砍專案重練
1.resources :可數 (例如剛剛取fishs應該要改成fishes)
2.model:單數 (例如剛剛取fish應該要改成fishe)
3.create_table :可數 (例如剛剛取fish應該要改成fishes)
還有"千萬別刪"migrate檔,會造成rake db:migrate出現錯誤
XX_index_path錯誤參考網址:http://stackoverflow.com/questions/23819312/rails-undefined-method-user-index-path
最後來執行網頁發現可以增加與看結果
底下主標題被痞客轉型變字超小,請不要在意他
https://rails-maro150cm.c9.io/fishes
fish list
- #<Fish:0x00000006142b10>
- fish1
- #<Fish:0x000000061424d0>
- fish2
add fish add fish
後來詢問專家有一個方法,可以解救我這連fish都以為可數的英文白癡
rails console <<進入Rails命令機狀態
"fish".pluralize <<詢問Rails對此單字複數是?
=> "fish" <<果然我白癡,不~~~~~
如果要離開Ctrl+D
ERB Embedded Ruby
簡單來說就是在網頁執行Ruby,以下有三種,不過常用都上面兩個
<% %> 只執行程式碼,不顯示出來
<%= %> 不僅執行程式碼,還需要顯示出來
<%# %> 單純Ruby註解,給自己看的
資料表關連
我們都知資料表關連有三種,分別為一對多、多對一、多對多,至於要打造關連是在Model內設定,說真的挺讓我意外!
首先我們先來創兩個Model,一個是產品另一個是商店,關係先設定一個商店有N個產品
rails g model Product
rails g model Store
接著來到db/migrate下找到我們兩個剛生出支架rb檔,增加資料表的欄位和屬性
create_table :products do |t|
t.string :title
t.text :description
t.integer :price
t.integer :store_id
create_table :stores do |t|
t.string :name
下個rake db:migrate成功加入Schema內一員後,開始增加Model內容
class Product < ActiveRecord::Base
belongs_to :store #belongs_to :PK (唯一值欄位)
end
class Store < ActiveRecord::Base
has_many :products #has_many :FK(重複值欄位)
end
透過Rails Console來確認是否成功
rails c
Product.create(title:"ASP.NET 1",price:100)
訊息說他自動產生以下SQL語法
(0.2ms) begin transaction
SQL (1.0ms) INSERT INTO "products" ("title", "price", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["title", "ASP.NET 1"], ["price", 100], ["created_at", "2015-03-16 13:57:23.843995"], ["updated_at", "2015-03-16 13:57:23.843995"]]
(15.3ms) commit transaction
=> #<Product id: 1, title: "ASP.NET 1", description: nil, price: 100, store_id: nil, created_at: "2015-03-16 13:57:23", updated_at: "2015-03-16 13:57:23">
Store.create(name:"ASP.NET Home")
訊息說他自動產生以下SQL語法
(0.1ms) begin transaction
SQL (1.2ms) INSERT INTO "stores" ("name", "created_at", "updated_at") VALUES (?, ?, ?) [["name", "ASP.NET Home"], ["created_at", "2015-03-16 13:58:54.702741"], ["updated_at", "2015-03-16 13:58:54.702741"]]
(18.9ms) commit transaction
=> #<Store id: 1, name: "ASP.NET Home", created_at: "2015-03-16 13:58:54", updated_at: "2015-03-16 13:58:54">
這時來看看Product內有多少資料...
Product.all
Product Load (1.0ms) SELECT "products".* FROM "products"
=> #<ActiveRecord::Relation [#<Product id: 1, title: "ASP.NET 1", description: nil, price: 100, store_id: nil, created_at: "2015-03-16 13:57:23", updated_at: "2015-03-16 13:57:23">, #<Product id: 2, title: "ASP.NET 2", description: nil, price: 200, store_id: nil, created_at: "2015-03-16 13:58:17", updated_at: "2015-03-16 13:58:17">]>
要實驗下方需再生一兩個產品,個人已經先做好了
p1=Product.find(1) <<id:1的Product用p1實體
p2=Product.find(2)
s1=Store.find(1)
p1.store=s1 <<把Product store_id 1跟Store id 1連一起
p2.store=s1
s1.products <<列出Store目前有連一起產品的資料
=> #<ActiveRecord::Associations::CollectionProxy []>
@@看不懂這怪玩兒,於是需要安裝新套件來幫助我們-----"Hirb"
先Ctrl+D跳出Rails console,把Gemfile檔案打開,下方增加....
gem 'hirb-unicode'
接著下"bundle update"命令,讓Rails專案重新讀取Gemfile檔並安裝
跑完之後繼續把上面步驟重做一遍(因為Console只是測試),記得要下'Hirb.enable'命令才能打開功能
Product Load (1.1ms) SELECT "products".* FROM "products" WHERE "products"."store_id" = ? [["store_id", 1]]
+--+-------------+-------------+-------+----------+--------------+---------------+
| id | title | description | price | store_id | created_at | updated_at |
+--+-------------+-------------+-------+----------+--------------+---------------+
| 1 | ASP.NET 1 | | 100 | 1 | 2015-03-16 13:57:23 UTC | 2015-03-16 14:04:41 UTC |
| 2 | ASP.NET 2 | | 200 | 1 | 2015-03-16 13:58:17 UTC | 2015-03-16 14:04:44 UTC |
+--+-------------+-------------+-------+----------+--------------+---------------+
2 rows in set
排的好整齊,看起來真好!T^T
既然一對多多對一能用belongs_to和has_many做出,那多對多也不用說!
根據資料庫原則,關於多對多是增小三...不對...是一個總管理表,因此是連到Product,Store的共兩個FK欄位,最重要此資料表PK(Rails上會自動生id,無視)
增資料表=要先增Model
rails g model StoreProductManager
create_table :store_product_managers do |t|
t.belongs_to :product # t.integer :product_id FK
t.belongs_to :store # t.integer :store_id FK
t.belongs_to意思這是來自一個資料表id,而我要跟隨他,注意後面欄位名稱是單數(慣例很重要,不聽話就砍專案)
關於前面我們在Product多的store_id要刪除,因此要多migrate寫方法改寫Schema
rails g migration delete_store_id_fromProduct
class DeleteStoreIdFromProduct < ActiveRecord::Migration
def change
remove_column:products,:store_id
end
end
rake db:migrate 後會發現
== 20150316144129 CreateStoreProductManagers: migrating =======================
-- create_table(:store_product_managers)
-> 0.0038s
== 20150316144129 CreateStoreProductManagers: migrated (0.0041s) ==============
== 20150316145223 DeleteStoreIdFromProduct: migrating =========================
-- remove_column(:products, :store_id) <<有從product刪除store_id欄位
-> 0.0098s
== 20150316145223 DeleteStoreIdFromProduct: migrated (0.0101s) ================
當然在Model上也是要改一些
class StoreProductManager < ActiveRecord::Base
belongs_to :product
belongs_to :store
end
class Product < ActiveRecord::Base
has_many :store_product_managers
has_many :stores,:through => :store_product_managers #don't through store_product_managers <<運作中不經過store_product_managers
end
class Store < ActiveRecord::Base
has_many :store_product_managers
has_many :products,:through => :store_product_managers #don't through store_product_managers <<運作中不經過store_product_managers
end
接著回到Rails Console,達成多對多又要增加資料
Product.create(title:"Ruby",price:500)
Store.create(name:"Computer Home")
把5個通通生出實體後,開始連結
s1.products=[p1,p2]
s2.products=[p2,p3]
把Hirb.enable開啟後
s1.products
+----+-----------+-------------+-------+------------------+------------------+
| id | title | description | price | created_at | updated_at |
+----+-----------+-------------+-------+------------------+------------------+
| 1 | ASP.NET 1 | | 100 | 2015-03-16 13:57:23 UTC | 2015-03-16 14:04:41 UTC |
| 2 | ASP.NET 2 | | 200 | 2015-03-16 13:58:17 UTC | 2015-03-16 14:04:44 UTC |
+----+-----------+-------------+-------+------------------+------------------+
2 rows in set
s2.products
+----+-----------+-------------+-------+-----------------+-------------------+
| id | title | description | price | created_at | updated_at |
+----+-----------+-------------+-------+-----------------+-------------------+
| 2 | ASP.NET 2 | | 200 | 2015-03-16 13:58:17 UTC | 2015-03-16 14:04:44 UTC |
| 3 | Ruby | | 500 | 2015-03-16 15:21:38 UTC | 2015-03-16 15:21:38 UTC |
+----+-----------+-------------+-------+-----------------+-------------------+
2 rows in set
p2.stores
+----+---------------+-------------------------+-------------------------+
| id | name | created_at | updated_at |
+----+---------------+-------------------------+-------------------------+
| 1 | ASP.NET Home | 2015-03-16 13:58:54 UTC | 2015-03-16 13:58:54 UTC |
| 2 | Computer Home | 2015-03-16 15:20:56 UTC | 2015-03-16 15:20:56 UTC |
+----+---------------+-------------------------+-------------------------+
更改Schema.rb內容
假如今天顧客突然要改欄位名稱,或是屬性換掉,還是自己手殘打錯,千萬不可以直接改或刪掉!(其實是為了要知道偷改過程...)
改欄名
rails g migration rename_column
class RenameColumn < ActiveRecord::Migration
def change
rename_column:books,:title,:name #rename_column:資料表,:原欄名,:新欄名
end
end
改欄屬
rails g migration change_column
class ChangeColumn < ActiveRecord::Migration
def change
change_column:books,:title,:text #change_column:資料表,:欄名,:新屬性
end
end
增加欄位
class AddStoreId < ActiveRecord::Migration
def change
add_column:products,:store_id,:integer #add_column:資料表,:欄名,:屬性
end
end
刪除欄位
class DeleteStoreId < ActiveRecord::Migration
def change
remove_column:products,:store_id #remove_column:資料表,:欄名
end
end
快速美化頁面與表格分頁
既然Rails是以幾分鐘生出CRUD(增讀改刪)聞名,那美化部分當然就用bootstrap-sass外掛套件(就是bootstrap加上sass)
首先在Gemfile加入並存檔
gem 'bootstrap-sass', '~> 3.3.4' #bundle update
然後下bundle update指令讓他重讀Gemfile檔,記得連正在跑的伺服器也要重開
等他安裝完成後,我們可以在css和js檔載入
stylesheets/application.scss <<副檔名改為scss
/*
*= require_tree .
*= require_self
*/
@import "bootstrap-sprockets";
@import "bootstrap";
javascripts/application.js
//= require jquery
//= require jquery_ujs
//= require turbolinks
//= require_tree .
//= bootstrap-sprockets
使用bootstrap-sass參考網址:https://github.com/twbs/bootstrap-sass
接著來說說關於Rails的網頁模組,簡單來說就是以前學過的框架
layouts/application.html.erb
<!DOCTYPE html>
<html>
<head>
<title>ModelNewbook</title> <<每頁都會顯示標題
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true %>
<%= javascript_include_tag 'application', 'data-turbolinks-track' => true %>
<%= csrf_meta_tags %>
</head>
<body>
<div class="container"> <<套用bootstrap css
<%= yield %> <<共同區塊,例如載入index內容,new內容...
</div>
</body>
</html>
其實在ASP.NET也有MasterPage(T^T當初不知這個做了好幾十個頁面),但是Rails恐怖在於連表單都能模組化,真是貫徹DRY精神!(讓我們不要放棄Ruby and Rails)
_form.html.erb <<表單頁面
edit.html.erb <<編輯頁面
<h1>Editing Namecard</h1>
<%= render 'form' %> <<載入表單頁面
<%= link_to 'Show', @namecard %> |
<%= link_to 'Back', namecards_path %>
最後我們要顯示分頁按鈕,感謝Kaminari大大願意共享,讓腦細胞少壞幾個
gem 'kaminari' #bundle update <<分頁功能
gem 'bootstrap-kaminari-views' #bundle update <<分頁美化功能
使用分頁在Controller檔
namecards_controller.rb
@namecards = Namecard.page(params[:page]).per(3)
當然在網頁檔也要增加
<%= paginate @namecards %> <<單純分頁按鈕,沒套用bootstrap
<%= paginate @namecards,:theme => 'twitter-bootstrap-3' %> <<套用bootstrap
使用分頁參考網址:https://github.com/amatsuda/kaminari
分頁美化參考網址:https://github.com/matenia/bootstrap-kaminari-views
來看看最後網頁美化結果
form形式的dropdownlist
我們知道下拉式清單有兩種,差別跟連不連資料表有關
第1種不用連資料表,以下為範例
<select name="selection"> <<Html input需要用name="selection",不然會無法傳遞參數
<option value="1">我是選項1</option>
<option value="2">我是選項2</option>
</select>
第2種要連資料表,以下為範例
<%= f.collection_select(:event_id,Event.all,:id,:event_title) %>
<%= f.collection_select(FK欄位,運作模組,傳入值,顯示值) %>
f是前頭<%= form_for(@invite) do |f| %>
FK欄位:event_id是在invites資料表中,身分是FK欄位連接events資料表
運作模組:Event.all是指透過模組Event顯示全部
傳入值:id是指events資料表內鍵id
顯示值:event_title是指events資料表
限制輸入框
這些限制輸入都寫在model中,而不是在view
首先最常用就是不准NULL、空白
validates :person_email, presence: { message: '請填寫 Email' }
再來用regular expression限制輸入格式
validates :person_email, presence: { message: '請填寫 Email' },
format: { with: /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i, message:'Email 格式有誤' }
最後是強迫使用者不准輸入同個值
validates :person_email, presence: { message: '請填寫 Email' },
format: { with: /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i, message:'Email 格式有誤' },
uniqueness: { scope: [:event_id], message: '這個 Email 已經填寫過了' }
Validates Format驗證格式
某天我好奇把以前Asp.Net的規則運算式拿出來測試看看,果然Rails跑出錯誤
/home/ubuntu/workspace/10236010moneydetails/app/models/cost.rb:4: syntax error, unexpected $undefined, expecting ']' ... format: {with: ([\u4e00-\u9fa5]{0,}|[A-Za-z]{0,... ... ^ /home/ubuntu/workspace/10236010moneydetails/app/models/cost.rb:4: syntax error, unexpected { arg, expecting ')' ... ([\u4e00-\u9fa5]{0,}|[A-Za-z]{0,}|[0-9]{0,})+$ ,message:'... ... ^ /home/ubuntu/workspace/10236010moneydetails/app/models/cost.rb:4: syntax error, unexpected { arg, expecting ')' ...\u9fa5]{0,}|[A-Za-z]{0,}|[0-9]{0,})+$ ,message:'細項格式... ... ^ /home/ubuntu/workspace/10236010moneydetails/app/models/cost.rb:4: `$ ' is not allowed as a global variable name
痾這一長串錯誤在搞啥,其實他意思是說你不符合Rails驗證規則
原來在format有規定的,以下為格式
/\A中間為規則運算式\z/
最前"/"和最尾"/"就類似Asp.Net的" "來包覆規則,至於\A和\z是為開頭到結尾
Rails驗證規則參考網址:http://apidock.com/rails/ActiveModel/Validations/HelperMethods/validates_format_of
規則運算式簡介參考網址:https://support.google.com/analytics/answer/1034324?hl=zh-Hant
強大Try
說句實話之前碰其他語言,本人根本沒寫過Try,因為個人覺得要控制輸入者乖乖打輸入框
當然不是只有輸入框才要Try,其實提示輸入者你做錯我是常寫的,說這麼多反正要講Rails Try
以下為抓來範例的表格
<tbody>
<% @invites.each do |invite| %>
<tr>
<td><%= invite.person_name %></td>
<td><%= invite.person_email %></td>
<td><%= invite.event.event_title %></td>
</tr>
<% end %>
</tbody>
如果這時我把events資料表一筆會影響這顯示的資料刪掉,相信你會看到錯誤
Showing /home/ubuntu/workspace/hasevent/app/views/invites/index.html.erb where line #24 raised:
undefined method `event_title' for nil:NilClass
22 23 24 25 26 27 |
<td><%= invite.person_name %></td>
<td><%= invite.person_email %></td>
<td><%= invite.event.event_title %></td>
</tr>
<% end %>
</tbody>
|
因為我把events資料通通刪光光,所以當他跑到invite的親家event找event_title發現是Null,毫不猶豫直接送你錯誤!
這時挖咧又要寫大串try,如果每個都要這樣玩,那一點都不DRY!
於是Rails送給我們-----try方法
<td><%= invite.event.try(:event_title) %></td>
接著可以看到Invite Event那欄的值是空白
Person Name | Person Email | Invite Event | |||
---|---|---|---|---|---|
Happy | have@yahoo.com |
相對路徑的圖片
如果圖片要用相對路徑的話,在Rails上會超級大不同!
當初在套用時一直出不了圖,想說奇怪我寫那麼久相對路徑(p.s. 是在寫Jsp或Asp.Net網頁)怎麼會有問題!
後來上網爬個文章,才知Rails img套用相對路徑會很麻煩,首先是.....
1.一定要app/assets/images底下
2.<img src="/assets/logo.png" alt="Logo">
難怪我看一些網頁他們路徑是用絕對路徑,說實話這時才知原因
img_tag參考網址:http://blog.csdn.net/wanghaoming100/article/details/9141863
製作後台路徑
如果要做後台路徑,首先在routes.rb透過namespace方法製作出來
routes.rb
namespace :back do
resources :blogs
end
然後在終端機用rake routes可以發現他路徑改變
back_blogs GET /back/blogs(.:format) back/blogs#index
如果你曾有用其他程式語言寫過,應該知道路徑前面back是資料夾
因此在終端機下新增controller指令時,可以在很多地方發現有新大陸
rails g controller back/blogs
1.controller路徑改變與class有Back
路徑為controllers/back/blogs_controller.rb
class Back::BlogsController < ApplicationController
2.helpers同controller狀況
3.views路徑改變
路徑為views/back/blogs
最後在views頁面需要改變有兩點
1.path改變,其實在rake routes中可以看出他變長
new_back_blog GET /back/blogs/new(.:format) back/blogs#new
2.form for改變,需要用陣列包裝並呼叫back這個namespace
<%= form_for [:back,@blog] do |f| %>
注意:Model不用照路徑走,依舊是Blog
DIY之2(示範寫出CRUD)
一直嘗試不用Scaffold寫出,但是卡住destory的路徑很久,後來才搞出來
根據觀察Scaffold可以知道運作流程
C--新增(其實沒show轉到首頁也OK,但是常理要給人家看增加資料)
在controller需要
before_action :set_blog, only: [:show]
def new
@blog=Blog.new
end
def create
@blog = Blog.new(blog_params)
if @blog.save
render :show
else
render :new
end
end
private
def set_blog
@blog = Blog.find(params[:id])
end
def blog_params
params.require(:blog).permit(:title, :author)
end
在views上需要new.html.erb
在views上需要show.html.erb外加內容
<p>
<strong>Title:</strong>
<%= @blog.title %>
</p>
<p>
<strong>Author:</strong>
<%= @blog.author %>
</p>
R--讀取
在controller需要
before_action :set_blog, only: [:show]
def show
end
private
def set_blog
@blog = Blog.find(params[:id])
end
在views上需要show.html.erb外加內容
<p>
<strong>Title:</strong>
<%= @blog.title %>
</p>
<p>
<strong>Author:</strong>
<%= @blog.author %>
</p>
U--更新
在controller需要
before_action :set_blog, only: [:show, :edit, :update]
def edit
end
def update
if @blog.update(blog_params)
render :show
else
render :edit
end
end
private
def set_blog
@blog = Blog.find(params[:id])
end
在views上需要edit.html.erb
D--刪除
在controller需要
before_action :set_blog, only: [:show, :edit, :update,:destroy]
def destroy
@blog.destroy
redirect_to back_blogs_path
end
private
def set_blog
@blog = Blog.find(params[:id])
end
在index.html.erb
<%= link_to 'Delete',back_blog_path(blog), method: :delete, data: { confirm: 'Are you OK?' } %>
相信大家也看出來,根據邏輯來說讀取,更新,刪除是要有id的
所以我們才需要
private
def set_blog
@blog = Blog.find(params[:id])
end
那在什麼時候開始查詢,當然是最前,而且僅在讀取,編輯,更新,刪除這狀態下路徑可用
before_action :set_blog, only: [:show, :edit, :update,:destroy]
不同頁面傳值問題
最近做"月帳表"有點小挫折,原因在於觀念上
例如想說在controller寫個值
def show
@re_mon=@cost.cost_date.strftime("%m").to_i-1
end
想說這樣可以在index.html上JQuery取到值 <<異想天開
當然結果是沒有的,因為他僅能在show頁面取到,就算在index中有同名變數,那只會被當成同名不同人
宣告變數僅在同def的頁面使用,不同頁面時需要用網址傳遞
例如說我們常常看到網址上有http://www.XXXX.XXX?a=XXX&b=XXX
因此是在轉址時做的....
redirect_to action: :index, re_mon: @cost.cost_date.strftime("%m").to_i-1
redirect_to是轉址
action是想連結過去的網頁
re_mon是自己增加標籤,也是附加在網址上參數
再來這串程式碼放show其實不恰當,因為是在create和update中才會想要立即在index看到
最後是在controller index中接收網址參數
@re_mon = params[:re_mon]
至於@re_mon是在JQuery中
<script>
$(document).ready(function(){
$('.nav li:eq(<%= @re_mon %>) a').tab('show')
});
</script>
參數參考網址:http://rails.ruby.tw/action_controller_overview.html
redirect_to參考網址:http://apidock.com/rails/ActionController/Base/redirect_to
datetime轉換格式
如果要把datetime轉成不同型態,可以用.strftime
屬性datetime欄位或變數.strftime("XXXX")
舉個小例子
@date.strftime("%Y-%m-%d")
這樣他就會轉成2015-04-16
根據doc我列出常用幾類:
%Y 西元年,4位數
%m 月份,2位數
%B 月份,英文字
%d 日號,2位數
%j 一年中天數,1~366 <<疑不是365天?
%H 24小時制,2位數
%I 12小時制,2位數
%P 顯示"am"或"pm"
%M 分鐘,2位數
%S 秒數,2位數
%A 星期,英文字
strftime參考網址:http://ruby-doc.org/core-2.2.1/Time.html#method-i-strftime
render載入
當用scaffold架起簡單網站時,可以發現render是載入意思
例如一: new,edit的html有寫到載入form
<%= render 'form' %>
form是指"_form.html.erb",前面底線檔名跟CoC有關
例如二: controller中寫到載入某標籤
render :new
根據標籤會載入new.html.erb這頁面
當然render不止這些,後面可以再加料...
1.partial(部分樣板),意思是抽出想要網頁放入這裡
其實我們很常用,像<%= render 'form' %>他的長版寫法是<%= render partial: 'form'%>
2.locals(當地參數),意思是設個參數與值給即將載入網頁
在原網頁寫<%= render partial: 'form',locals:{name:'apple'} %>
在即將載入網頁寫<%= name %>
3.collection跟as(集合與個體),意思是陣列與陣列內成員
<%= render partial: 'form',collection:@books,as: :book %>
跟<% @blogs.each do |blog| %>同樣,把寫在一個頁面分割成二個頁面,達到能共用的愉悅感
flash與session
在支架controller有寫這段
format.html { redirect_to :index, notice: 'Cost was successfully created.' }
這樣寫法挺糟糕,因為在index網址上出現?notice=XXXXXX,中間傳遞參數直接給眾人看,可能會造成資安危機
因此我們改用flash解決問題
1.在轉址前傳遞參數
controller
flash[:notice]="Cost was successfully created."
format.html { redirect_to :index }
2.在頁面透過flash接收
view
<%= flash[:notice]%>
既然flash這麼方便,那是不是能當session呢?當然是不行的!
其實flash運作方式跟session大同小異,只差flash存活一瞬間,接收成功後就丟棄
要存活長時間還是session,使用步驟跟flash一樣
1.在轉址前傳遞參數
controller
session[:myid]="XXXX"
format.html { redirect_to :index }
2.在頁面透過flash接收
view
<%= session[:myid]%>
指定layout版面
1.Controller最上面,指定全部Views套用此layout
layout "檔名"
當然也可僅限在特定action
layout "檔名", only: [:index,:show...]
2.Render時,針對一些特殊狀態改變版面
render layout: '檔名',action: :index
routes中as用處
在render中有出現collection跟as,意思是集合中一個成員
但在routes也能用as,不過意思是Prefix取名
get 'back/blogs',to: 'back/blogs#index',as: :get_index
用rake routes可以發現
Prefix Verb URI Pattern Controller#Action
get_index GET /back/blogs/index(.:format) blogs#index
為何要幫一個絕對路徑取名?
當然是假如有一天路徑突然改名稱,那不就每個有寫到 link_to 絕對路徑要改!
所以我們要用as,讓網頁寫法不再絕對路徑,而是相對路徑!
<%= link_to "get",get_index_path %>
Helper
看到Helper內容空空,還以為他沒做什麼事,其實你用的很多是靠小幫手(Helper)
例如:
link_to "link name",path <<UrlHelper
image_tag "file name",alt: "img",size: '300x300' <<AssetTagHelper
tag :br <<TagHelper
<%= content_tag :p,class: 'text' do%> <<TagHelper
<%=book.title%>
<% end %>
<%= form_tag url_for(action: :create) do%> <<FormTagHelper
<% end %>
接著我們要寫一段Helper,達到寫程式簡化
def link_img(img_url,to_url)
link_to image_tag(img_url),to_url
end
在網頁就能透過Helper呼叫link_img方法
link_img("http://...",books_path)
多國語系
跟android思想同樣,希望語系可以切換
首先語系在config/locales/en.yml <<rails預設為en
en:
activerecord:
attributes:
blog: <<這裡blog是model blog檔名
hello: 作者
hello2: 玩家
接著在網頁上用human_attribute_name指定yml中標籤
<%= Blog.human_attribute_name :hello%>
<%= Blog.human_attribute_name :hello2%>
寄Email
1.Gmail篇(結果GG)
在Rails有Mailer可以設定Email,接著搭配SMTP Server就行!(不過在Gmail很艱辛...)
首先在終端機下新增指令
rails g mailer mail
P.S. 其實要取"專門為什麼事而做"的變數名稱,不應該取"mail"這不明不白名稱,個人是因為"測試寄信"關係才取
如果很熟MVC的話,就算跳到Mailer也不用怕,以下舉application和樣板mailer寫法可以發現運作一樣!
mailers/application_mailer.rb
class ApplicationMailer < ActionMailer::Base
default from: "haveagoodday8496@gmail.com" <<預設寄送Email地址
layout 'mailer' <<樣板套用mailer
end
接著觀看樣板layouts/mailer.html.erb
<html>
<body>
<%= yield %> <<不用說當然套用views/mail_mailer/XXX.html.erb
</body>
</html>
既然知道就回到主軸,第二步要撰寫Mailer Controller的方法
mailers/mail_mailer.rb
class MailMailer < ApplicationMailer
def mail_notify(name,email,note)
@name=name
@mail=email
@note=note
mail(to: email,from:'haveagoodday8496@gmail.com',subject:'Test Mail') #send email
end
end
第三步新增網頁檔並撰寫內容
views/mail_mailer/mail_notify.html.erb
Hello,Hello,Hello,<%= @mail %><br /><br />
your name is <%= @name %> ,and <br />
your note is <%= @note %>
第四步環境設定(根據不同狀態選擇development.rb還production.rb)
以下是個人設定在development環境,利用gmail環境自己寄自己收
config.action_mailer.raise_delivery_errors = true
config.action_mailer.perform_deliveries = true
config.action_mailer.default_url_options = { :host => 'localhost:3000' }
config.action_mailer.default :charset => "utf-8"
config.action_mailer.delivery_method = :smtp
config.action_mailer.smtp_settings = {
address:'smtp.gmail.com',
port:587,
domain:'gmail.com',
user_name:'login gmail user_name',
password:'login gmail password',
authentication:'plain',
enable_starttls_auto: true
}
第五步在主要Controller設定寄送機關,以下是當index跳到calc頁面時開啟機關
controllers/mails_controller
class MailsController < ApplicationController
def index
end
def calc
@name=params[:name] #params is string
@email=params[:email]
@note=params[:note]
#render text:params
MailMailer.mail_notify(@name,@email,@note).deliver_now
#MailMailer.mail_notify(@name,@email,@note).deliver_later
end
end
最終個人在gmail的收信箱找到,卻是警告信! T^T
Gmail官方宣言:"有個駭客想用你的帳密寄信,我們已經阻止他!"
Gmail詳細資料:"有個來自XXX.XXX.XXX.XXX的IP試圖強行登入,我們已經阻止他!"
可惡別阻止阿娘強入Google,我要成功拉!!!
2.Mandrill篇(結果GG)
其實會要寄信都是大量的,因此直接學如何使用第三方SMTP如Mandrill比較實在!XD
只要更改smtp設定
config.action_mailer.smtp_settings = {
address:'smtp.mandrillapp.com',
port:587,
domain:'XXX.com',
user_name:'login mandrill email',
password:'API Key',
authentication:'plain',
enable_starttls_auto: true
}
以上兩篇結論:
原因在於
1.要真的Server! T^T (個人就是窮學生哪來Server)
2.即使丟到heroku app連接像heroku.com domain還是沒辦法! T^T (個人沒錢拿來升級帳戶阿)
Mandrill+Heroku通關方法:https://devcenter.heroku.com/articles/mandrill
個人根據通關方法卡在第一步,於是開始爬文
發問區:http://stackoverflow.com/questions/23319451/heroku-mandrill-addon-mandrill-starter-add-on-for-heroku-installation-failed
根據回答者表示:升級帳戶意思就是叫你拿錢贊助他們!
3.Mandrill Template+Rails Console篇(美中不足)
樣板部分
程式部分
更改一:gemfile增加內容並bundle install
gem 'mandrill-api',require: "mandrill"
更改二:增加mandrill_client方法,運用message傳送參數給Mandrill Template
mailers/mail_mailer.rb
class MailMailer < ApplicationMailer
def mandrill_client
@mandrill_client ||= Mandrill::API.new Mandrill_Api_Key
end
def mail_notify(name,email,note)
#@name=name
#@mail=email
#@note=note
#mail(to: email,from:'haveagoodday8496@gmail.com',subject:'Test Mail')
#use Mandrill Template Send Email
template_name = "test-mail"
template_content = []
message = {
to: [{email: email}],
subject: "This is May send to you,her email is #{email}",
auto_text: true,
#this is have to use to everyone.
global_merge_vars: [
{
name: "CompanyName",
content: "Nesting Doll Emporium"
},
{
name:"HELLO",
content: name
},
{
name:"EMAIL",
content: email
},
{
name:"NOTE",
content: note
}
]
}
mandrill_client.messages.send_template template_name,template_content,message
end
end
更改三:環境設定
個人是development.rb
Mandrill_Api_Key = "your API Key"
Rails.application.configure do
....
....
config.action_mailer.raise_delivery_errors = true
config.action_mailer.perform_deliveries = true
config.action_mailer.default_url_options = { :host => 'localhost:3000' }
config.action_mailer.default :charset => "utf-8"
config.action_mailer.delivery_method = :smtp
config.action_mailer.smtp_settings = {
address:'smtp.mandrillapp.com',
port:587,
domain:'heroku.com',
user_name:'haveagoodday8496@gmail.com',
password: Mandrill_Api_Key,
authentication:'plain',
enable_starttls_auto: true
}
end
執行結果
在終端機下指令
$ rails c
> MailMailer.mail_notify("yoyo","haveagoodday8496@gmail.com","Hello")
現在寄 VS 待會寄
中途大家應該有發現...
MailMailer.mail_notify(@name,@email,@note).deliver_now
跟MailMailer.mail_notify(@name,@email,@note).deliver_later
.deliver_now現在寄:發個問題等人回答,等確定回答後才會跳到下個頁面,會讓使用者不耐煩,以為伺服器很差!((喂
.deliver_later待會寄:發個問題等人回答,不用等有回答直接跳到下個頁面,會讓使用者不會煩,而且會覺得順暢!((疑
對使用者來說,看到結果頁面比收到信還重要!!((遭毆
簡單來說待會寄是個優勢,因此有兩個提供待放服務...
1.Redis => 串接RAM 需要架Redis Server ,這效能好
2.delayed-Job => 串接硬碟 需要gem套件
個人(窮學生)直接考慮用delayed-job!
delayed-job GitHub(附送使用說明):https://github.com/collectiveidea/delayed_job
個人是用在Active Record,因此gemfile檔是增加這行
gem 'delayed_job_active_record'
如果要讓他像背景程式一樣一直執行,那要多安裝這個才能
gem "daemons"
接著在終端機下bundle install
完畢後開始創個草稿資料表與存成正式資料表
rails generate delayed_job:active_record
rake db:migrate
最後只要在config/application.rb加入這行就OK!
module Mail
class Application < Rails::Application
...
...
config.active_job.queue_adapter = :delayed_job <<請把他放在這裡
end
end
SQL語句
最常看到model.all這種"搜尋一個資料表的全部資料",但不是完全適用一些狀況,以下我列出常用的語句......
其實跟以前學的SQL差不多,只是在Rails可以偷懶一下下
1.where
model.where(["欄名1 = ? and 欄名2 = ? and ...",第一個?對應參數,第二個?對應參數,...])
等同於
Select * From model對應table Where 欄名1=第一個參數 and 欄名2=第二個參數....
2.order
model.order("欄名1 desc,欄名2 asc,...")
等同於
Select * From model對應table Order by 欄名1 desc,欄名2 asc,...
3.select
model.select("欄名1,欄名2,...") 等同於 Select 欄名1,欄名2 From model對應table
model.select("*") 等同於 Select * From model對應table
model.select("欄名").distinct 等同於 Select DISTINCT 欄名 From model對應table
4.group
model.select("....").group("欄名") 等同於 Select ... From model對應table Group by 欄名
5.having
model.select("....").group("欄名").having(["欄名1 = ? and 欄名2 = ? ...",第一個?參數,第二個?參數...])
等同於
Select ... From model對應table Group by 欄名 Having 欄名1=第一個參數 and 欄名2=第二個參數...
6.joins
model.joins("Left outer join table1 on table1.欄名1=model對應table.欄名1...") <<也可以使用Right outer join,inner join
等同於
Select model對應table.* From model對應table Left outer join table1 on table1.欄名1=model對應table.欄名1....
model.joins(:table1,...)
等同於
Select model對應table.* From model對應table inner join table1 on table1.欄名1=model對應table.欄名1....
7.組合
舉例1 model.joins("...").where("...").select("...")
舉例2 model.where("...").group("...").having("...")
舉例3 model.where("...").sum("...")
舉例4 model.where("...").count
...等等奇妙組合!!((喂
SQL語法參考網址:http://guides.rubyonrails.org/active_record_querying.html#joining-tables
問題主鍵是Id
第一次對Rails發瘋,因為他的PK直接預設id,不能讀也不能取
好吧其實沒關係,PK給你就給,反正我要的複合鍵RoR並不支援!!((翻桌
不過卻很妙的可以用"Model"解決複合鍵和String問題
model uniqueness限制使用者輸入,以下為範例
validates :欄名1, presence: { message: '請填寫 欄名1' },
uniqueness: { scope: [:欄名2,:欄名3], message: '這個 欄名1,欄名2,欄名3 已經填寫過了' }
用uniqueness and scope[add 1 column,add 2 column,...]達到送出前檢查資料唯一性,而且可以DIY錯誤訊息!!((默默把桌放正