Rails Web應用開發實戰-學生選課系統基礎版(三)

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

在這個控制器中一共定義了六個方法:

  1. new:負責創建一個新的課程,它的邏輯很簡單:即申明一個新的Course類的對象,然後交給視圖。
  2. create: 負責接收新創建課程的數據,然後保存,它的邏輯爲:先調用名爲course_paramsprivate類型的方法以獲得課程的數據,並傳入新創建的課程模型的對象。然後保存這個對象,而在保存這個對象時會進行對數據進行檢查,若檢查驗證通過(各項數據都存在且符合規範),即@course.save返回
    true,那麼頁面就會調用redirect_to方法重定向至course_path這個URL;若檢查驗證未通過或者保存過程中發生錯誤,即@course.save返回爲false,那麼會調用render方法,重新渲染new方法的視圖,讓用戶再次編輯提交
  3. index:負責對所有的對象進行展示,邏輯爲:在Course類中調用all方法返回所有課程對象,然後交給視圖
  4. edit:負責對一個已有的課程進行編輯修改,它的邏輯也很簡單:通過Course.find_by(id: params[:id])找到需要編輯的課程對象,其中params[:id]表示Rails從URL裏讀取的當前需要編輯課程的id,例如http://whatever.com/course/17中的17。
  5. update:負責接收更新修改後的課程數據,它的邏輯爲:通過Course.find_by(id: params[:id])找到當前已經修改的課程對象,然後調用update_attributes接收新的參數並更新,無論更新成功或者更新失敗,都重定向至courses_path
  6. 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的標籤,divclass屬性控制了每個標籤的渲染方式(通過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方法構造了一個表單,表單的提交對象是@coursef.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

發佈了46 篇原創文章 · 獲贊 132 · 訪問量 20萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章