Rails Web應用開發實戰-學生選課系統基礎版(三)
上次主要講了MVC中的模型,此教程我們開始涉及控制器和視圖。
1.路由
Rails 路由能識別 URL,將其分發給控制器的動作進行處理,還能生成路徑和 URL,無需直接在視圖中硬編碼字符串。
我們打開config/route.rb
文件,加入一行:
Rails.application.routes.draw do
resources :courses
end
然後在終端中運行:rake routes
查看路由地址,得到下面的輸出:
pengzhaoqing:~/workspace $ rake routes
Prefix Verb URI Pattern Controller#Action
courses GET /courses(.:format) courses#index
POST /courses(.:format) courses#create
new_course GET /courses/new(.:format) courses#new
edit_course GET /courses/:id/edit(.:format) courses#edit
course GET /courses/:id(.:format) courses#show
PATCH /courses/:id(.:format) courses#update
PUT /courses/:id(.:format) courses#update
DELETE /courses/:id(.:format) courses#destroy
以第一行爲例進行解釋:
/courses
指的是URL路徑,指定了用戶在此域名下要訪問的地址,例如:http://whatever.com/courses
GET
指訪問的過程中用的HTTP GET方法,詳見這裏courses#index
表示了當用戶訪問/courses
地址時,Rails將會把這個請求交給控制器courses_controller
中的index
方法處理- 而prefix字段對應的
course
代表了,URL地址在Rails中的表示形式,即在Rails中調用course_path
這個方法時,Rails將會解析返回一個字符串url:/courses
後面每一行都可以按照上面的四個小點進行解釋,而其中空的部分代表與上一行相等(如第二行中的prefix字段爲空,表示它的prefix與上一行相等,即也爲courses
)
一句簡單的
resources :courses
,Rails將會自動生成8個標準的URL地址,包括了CURD(創建Create、更新Update、讀取Retrieve和刪除Delete操作)這裏只是一個簡單的介紹,請參照Rails Routes來理解Rails的路由
2.課程控制器
控制器體現了Rails的邏輯實現,是整個web應用的核心,它處理着每個URl請求,而處理過程中調用了模型(數據庫),最終將結果交給給視圖,然後視圖經過一定的渲染返回給用戶的瀏覽器。
我們運行rails generate controller courses
來創建課程控制器:
pengzhaoqing:~/workspace $ rails generate controller courses
Running via Spring preloader in process 1699
create app/controllers/courses_controller.rb
invoke erb
create app/views/courses
invoke test_unit
create test/controllers/courses_controller_test.rb
invoke helper
create app/helpers/courses_helper.rb
invoke test_unit
invoke assets
invoke coffee
create app/assets/javascripts/courses.coffee
invoke scss
create app/assets/stylesheets/courses.scss
打開剛纔創建的app/controllers/courses_controller,填入以下代碼:
class CoursesController < ApplicationController
def new
@course=Course.new
end
def create
@course = Course.new(course_params)
if @course.save
redirect_to courses_path, flash: {success: "新課程申請成功"}
else
flash[:warning] = "信息填寫有誤,請重試"
render 'new'
end
end
def edit
@course=Course.find_by(id: params[:id])
end
def update
@course = Course.find_by_id(params[:id])
if @course.update_attributes(course_params)
flash={:info => "更新成功"}
else
flash={:warning => "更新失敗"}
end
redirect_to courses_path, flash: flash
end
def destroy
@course=Course.find_by_id(params[:id])
@course.destroy
flash={:success => "成功刪除課程: #{@course.name}"}
redirect_to courses_path, flash: flash
end
def index
@course=Course.all
end
private
def course_params
params.require(:course).permit(:course_code, :name, :course_type, :teaching_type, :exam_type,
:credit, :limit_num, :class_room, :course_time, :course_week)
end
end
在這個控制器中一共定義了六個方法:
- new:負責創建一個新的課程,它的邏輯很簡單:即申明一個新的Course類的對象,然後交給視圖。
- create: 負責接收新創建課程的數據,然後保存,它的邏輯爲:先調用名爲
course_params
private類型的方法以獲得課程的數據,並傳入新創建的課程模型的對象。然後保存這個對象,而在保存這個對象時會進行對數據進行檢查,若檢查驗證通過(各項數據都存在且符合規範),即@course.save
返回
爲true
,那麼頁面就會調用redirect_to
方法重定向至course_path
這個URL;若檢查驗證未通過或者保存過程中發生錯誤,即@course.save
返回爲false
,那麼會調用render
方法,重新渲染new
方法的視圖,讓用戶再次編輯提交 - index:負責對所有的對象進行展示,邏輯爲:在Course類中調用
all
方法返回所有課程對象,然後交給視圖 - edit:負責對一個已有的課程進行編輯修改,它的邏輯也很簡單:通過
Course.find_by(id: params[:id])
找到需要編輯的課程對象,其中params[:id]表示Rails從URL裏讀取的當前需要編輯課程的id,例如http://whatever.com/course/17
中的17。 - update:負責接收更新修改後的課程數據,它的邏輯爲:通過
Course.find_by(id: params[:id])
找到當前已經修改的課程對象,然後調用update_attributes
接收新的參數並更新,無論更新成功或者更新失敗,都重定向至courses_path
- destory:負責對已有的課程進行刪除,邏輯爲:找到要刪除的課程對象,執行刪除,重定向至
courses_path
這裏其實還有一個方法未寫進去:show,這個方法負責展示某個課程的詳細信息,邏輯與前面的edit方法相同。
這一共七個方法構成了Rails應用標準的增刪改查(CURD)四個功能:增爲new和create方法,刪爲destroy方法,改爲edit和update方法,查爲show方法。
另外,params方法能返回URL請求包含的所有數據,而flash作爲一個Hash表(哈希表),起到了重定向後提示語的作用,能在用戶操作後,告訴用戶的操作的結果(成功、失敗或者需要登錄等)。
3.課程視圖
用戶的請求經過了控制器裏某個方法處理後,會被交給這個請求對應的視圖。視圖一般由JS、CSS和HTML文件構成。
若完全不瞭解HTML,請移步這裏。
這裏我們使用Bootstrap前端庫,Boostrap是一個專門爲後端程序員設計的,可以讓一個不懂前端的程序員寫出比較良好的前端效果,其實就是一個CSS、JS庫。
有關bootstrap請看這裏
我們在將來會用到JS的標準庫JQuery,封裝了各種JS的方法。
有關JQuery請看這裏
在看下面的時候請確保對HTML和Bootstrap有一定的瞭解:
1.爲了使用這種外部庫,我們打開根目錄下Gemfile文件,在裏面加入:
# for bootstrap
gem 'bootstrap-sass', '~> 3.3.7'
# Use ActiveModel has_secure_password
gem 'bcrypt', '~> 3.1.11'
使用bundle install
安裝這個外部庫
2.在assets/stylesheets
中把application.css
刪除,增加application.scss
文件,並加入以下代碼來告訴Rails導入boostrap相關的css文件
@import 'bootstrap-sprockets';
@import 'bootstrap';
@import 'bootstrap/theme';
3.在assets/javascripts/application.js
中增加一行://= require bootstrap
,告訴Rails導入bootstrap相關的js文件
4.創建views/courses
文件夾,用來存放course_controller
的各個方法的視圖文件。
Rails默認的視圖文件類型爲
filename.html.erb
,並不是單純的HTML文件,這是因爲Rails需要在這個文件中做一些運算,但最終會編譯成HTML文件。
5.創建views/courses/new.html.erb
文件,在文件中寫入:
<div class="container-fluid">
<div class="row">
<div class="col-md-offset-3 col-sm-6">
<div class="panel panel-success">
<div class="panel-heading">
<h3 class="text-center">新課程</h3>
</div>
<div class="panel-body">
<%= render "courses/form" %>
</div>
</div>
</div>
</div>
</div>
這就是一個簡單的視圖文件,div
爲HTML的標籤,div
的class
屬性控制了每個標籤的渲染方式(通過boostrap)
上述代碼中,被括號和百分號包裹的語句會被ruby執行,例如
<%= 這裏的代碼會被ruby動態執行 %>
<%= render "courses/form" %>
這一句表示這裏將會渲染局部視圖courses/_form.html.erb
中的文件內容,也就是將courses/_form.html.erb
文件內容替換到這裏。因爲有些視圖代碼將會被使用多次,Rails爲了提高代碼的複用性,將這些代碼提取出來單獨成立一個文件,在其他地方調用即可。這種視圖被稱爲partial(局部視圖),partial在命名的時候前面要加上下劃線,如_form.html.erb
。
6.那麼我們接着創建view/courses/_form.html.erb
,填入以下代碼:
<%= form_for @course, html: {class: 'form-horizontal', role: 'form'} do |f| %>
<div class="form-group">
<%= f.label "課程名", class: 'col-sm-3 control-label' %>
<div class="col-sm-9">
<div class="input-group">
<div class="input-group-addon"><span class="glyphicon glyphicon-arrow-right"></span></div>
<%= f.text_field :name, class: 'form-control', placeholder: "輸入課程名" %>
</div>
</div>
</div>
<div class="form-group">
<%= f.label "課程屬性", class: 'col-sm-3 control-label' %>
<div class="col-sm-9">
<div class="input-group">
<div class="input-group-addon"><span class="glyphicon glyphicon-arrow-right"></span></div>
<%= f.text_field :course_type, class: 'form-control', placeholder: "輸入課程屬性" %>
</div>
</div>
</div>
<div class="form-group">
<%= f.label "授課方式", class: 'col-sm-3 control-label' %>
<div class="col-sm-9">
<div class="input-group">
<div class="input-group-addon"><span class="glyphicon glyphicon-arrow-right"></span></div>
<%= f.text_field :teaching_type, class: 'form-control', placeholder: "輸入授課方式" %>
</div>
</div>
</div>
<div class="form-group">
<%= f.label "考試方式", class: 'col-sm-3 control-label' %>
<div class="col-sm-9">
<div class="input-group">
<div class="input-group-addon"><span class="glyphicon glyphicon-arrow-right"></span></div>
<%= f.text_field :exam_type, class: 'form-control', placeholder: "輸入考試方式" %>
</div>
</div>
</div>
<div class="form-group">
<%= f.label "限選人數", class: 'col-sm-3 control-label' %>
<div class="col-sm-9">
<div class="input-group">
<div class="input-group-addon"><span class="glyphicon glyphicon-arrow-right"></span></div>
<%= f.text_field :limit_num, class: 'form-control', placeholder: "輸入限選人數" %>
</div>
</div>
</div>
<div class="form-group">
<%= f.label "課時學分", class: 'col-sm-3 control-label' %>
<div class="col-sm-9">
<div class="input-group">
<div class="input-group-addon"><span class="glyphicon glyphicon-arrow-right"></span></div>
<%= f.text_field :credit, class: 'form-control', placeholder: "輸入課時學分" %>
</div>
</div>
</div>
<div class="form-group">
<%= f.label "教室", class: 'col-sm-3 control-label' %>
<div class="col-sm-9">
<div class="input-group">
<div class="input-group-addon"><span class="glyphicon glyphicon-arrow-right"></span></div>
<%= f.text_field :class_room, class: 'form-control', placeholder: "輸入教室" %>
</div>
</div>
</div>
<div class="form-group">
<%= f.label "上課週數", class: 'col-sm-3 control-label' %>
<div class="col-sm-9">
<div class="input-group">
<div class="input-group-addon"><span class="glyphicon glyphicon-arrow-right"></span></div>
<%= f.text_field :course_week, class: 'form-control', placeholder: "輸入課時學分" %>
</div>
</div>
</div>
<div class="form-group">
<%= f.label "上課時間", class: 'col-sm-3 control-label' %>
<div class="col-sm-9">
<div class="input-group">
<div class="input-group-addon"><span class="glyphicon glyphicon-arrow-right"></span></div>
<%= f.text_field :course_time, class: 'form-control', placeholder: "輸入課時學分" %>
</div>
</div>
</div>
<%= f.submit '提交', class: "btn btn-success btn-block" %>
<%= link_to '取消', courses_path, :class => "btn btn-default btn-block" %>
<% end %>
除了爲美觀而加上div標籤,提取其主要內容爲:
<%= form_for @course do |f| %>
<%= f.label "課程名" %>
<%= f.text_field :name %>
.......
<%= f.submit '提交' %>
<%= link_to '取消', courses_path %>
<% end %>
form_for方法構造了一個表單,表單的提交對象是@course
,f.text_field
指定了提交的字段,f.submit
申明瞭提交的按鈕, <%= link_to '取消', courses_path %>
這一句爲表增加了一個鏈接,表示鏈接到了courses_path
這個URL,鏈接上顯示的是“取消”這兩個字。
更多的關於表單構造方式詳見表單helper
7.然後我們創建edit方法的視圖views/courses/edit.html.erb
,edit視圖與new視圖一樣,使用了courses/_form.html.erb
局部視圖:
<div class="container-fluid">
<div class="row">
<div class="col-md-offset-3 col-sm-6">
<div class="panel panel-success">
<div class="panel-heading">
<h3 class="text-center">更改課程: <%= @course.name %></h3>
</div>
<div class="panel-body">
<%= render "courses/form" %>
</div>
</div>
</div>
</div>
</div>
8.最後創建index方法的視圖views/courses/index.html.erb
:
<div class="container-fluid">
<div class="row">
<div class="col-md-offset-2 col-sm-8">
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title">課程列表</h3>
</div>
</div>
<div class="panel-body">
<table class="table table-responsive table-condensed table-hover">
<thead>
<tr>
<th>課程編號</th>
<th>課程名稱</th>
<th>課時/學分</th>
<th>考試方式</th>
</tr>
<tbody>
<% @course.each do |course| %>
<tr>
<td><%= course.course_code %></td>
<td><%= course.name %></td>
<td><%= course.credit %></td>
<td><%= course.exam_type %></td>
<td><%= link_to "編輯", edit_course_path(course), class: 'btn btn-xs btn-info' %></td>
<td><%= link_to "刪除", course_path(course), :method => "delete", class: 'btn btn-xs btn-danger', :data => {confirm: '確定要刪除此課程?'} %></td>
</tr>
<% end %>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
- index視圖功能是把所有的課程用表格形式列舉出來,並在每個課程後面加上編輯和刪除按鈕。
4.運行
1.點擊run project,在瀏覽器中輸入https://你的c9用戶名和項目名/courses/new
,例如:https://course-select-dev-pengzhaoqing.c9users.io/courses/new
,就能看到courses控制下new方法返回的視圖:
2.點擊提交,若成功會跳轉至index方法的頁面(https://course-select-dev-pengzhaoqing.c9users.io/courses
),列舉出所有的課程數據:
3.在每一個課程後面點擊編輯,會跳轉至編輯頁面(https://course-select-dev-pengzhaoqing.c9users.io/courses/2/edit
),再次點擊提交,若成功還會跳轉至index方法頁面:
4.在每一個課程後面點擊刪除,會刪除此課程,並跳轉至index方法頁面。
以上4步的邏輯,都是在courses_controller的各方法裏體現的,有心的學習者可以回頭看看控制器裏是怎麼實現跳轉的,並理解視圖與控制器還有視圖之間如何配合的
而且,這裏new和edit頁面都使用了相同的局部視圖,因此看起來沒有什麼差別,但卻用一個模板實現了兩種功能(新建和編輯),複用了代碼,省去開發人員重複編寫工作。
5.結束語
到這裏,基本的Rails框架就介紹全了,但是這還僅僅只是個很簡單的增刪改查功能,後面我們會介紹到用戶登錄功能,權限控制功能,都是在這個基礎上進行的,所以請務必將此教程理解,再進行下個教程。
如果理解有困難,請先跟着官方指南走一遍,再回頭看這個
若有疑問請在https://github.com/PENGZhaoqing/CourseSelect上提issue