目标
建立图书管理后台,实现图书CRUD、会员系统、 “上架与下架” 一键切换,借书单。
在本教程中两个符号 “*…” 中间插入的代码新增代码。
1、配置路由
设定后台图书的 CURD、前台图书的路由
config/routes.rb
Rails.application.routes.draw do
root 'welcome#index'
* resources :users
* resources :sessions
resources :books
namespace :admin do #使用命名空间 namespace
resources :books
end
* # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end
2、建立 Book 数据表
2.1 数据库中建立 book 数据表
$ rails g migration create_books
db/migrate/xxxx一堆数字xxxx_create_books.rb
class CreateBooks < ActiveRecord::Migration[5.1]
* def change
* create_table :books do |t|
t.string :title
t.text :text
t.timestamps
* end
* end
end
2.2 建立 book 模型
$ touch app/models/book.rb
app/models/book.rb
class Book < ApplicationRecord
end
2.3 测试
终端进入 rails c (后台金手指)
rails c #进入 rails 金手指
u = Book.new
u.title = "Hello"
u.text = "World"
u.save
exit #退出 rails 金手指
3、实现 Book 后台的 CRUD
3.1 Book的New
终端执行
mkdir app/controllers/admin
touch app/controllers/admin/books_controller.rb
配置文件 app/controllers/admin/books_controller.rb
class Admin::BooksController < ApplicationController
# 使用命名空间 namespace ,class的命名使用 Admin::BooksController 格式
def new
@book = Book.new
end
def create
@book = Book.new(book_params)
if @book.save
#查询单个数据必须传递参数实现检索定位,例如@book
redirect_to book_path(@book)
#重定向 >> 图书 show 页面
else
render "new"
end
end
private
#健壮参数,简化参数代码,实现 rails 预筛选安全机制。
def book_params
params.require(:book).permit(:title, :text)
end
end
终端执行
mkdir app/views/admin/books
touch app/views/admin/books/new.html.erb
配置文件 app/views/admin/books/new.html.erb
<h1>新书上架</h1>
#使用命名空间 namespace ,传递参数使用数组[:admin, @book]
<%= form_for [:admin, @book] do |f| %>
<p>
<%= f.label :title %>
<%= f.text_field :title %>
</p>
<p>
<%= f.label :text %>
<%= f.text_area :text %>
</p>
<p>
<%= f.submit "提交"%>
</p>
<% end %>
3.2 Book的show
修改文件 app/controllers/admin/books_controller.rb
class Admin::BooksController < ApplicationController
def index
@books = Book.all
end
* def new
* end
* def create
* end
def show
@book = Book.find(params[:id])
end
end
终端执行
book 后台新建的图书,要让所有人都看到,需要新建文件 app/views/books
mkdir app/views/books
touch app/views/books/show.html.erb
touch app/views/admin/books/index.html.erb
配置图书展示页 app/views/books/show.html.erb
<h1>图书简介</h1>
<p>
<strong>书名:</strong>
<%= @book.title %>
</p>
<p>
<strong>简介:</strong>
<%= @book.text %>
</p>
3.3 Book的edit
修改文件 app/controllers/admin/books_controller.rb
class Admin::BooksController < ApplicationController
......
* def show
* end
def edit
@book = Book.find(params[:id])
end
def update
@book = Book.find(params[:id])
if @book.update(book_params)
#查询某数据表所有数据,可以省略传递参数
redirect_to admin_books_path
else
render "edit"
end
end
......
end
终端执行
touch app/views/admin/books/edit.html.erb
配置文件 app/views/admin/books/edit.html.erb
<h1>图书修改</h1>
<%= form_for [:admin, @book] do |f| %>
<p>
<%= f.label :title %>
<%= f.text_field :title %>
</p>
<p>
<%= f.label :text %>
<%= f.text_area :text %>
</p>
<p>
<%= f.submit "提交"%>
</p>
<% end %>
配置图书后台首页 app/views/admin/books/index.html.erb
<h1>图书馆后台</h1>
<p>
<%= link_to "新书上架", new_admin_book_path %>
</p>
<table class="table table-bordered table-hover">
<tr class="text-info">
<th>书名</th>
<th>简介</th>
<th>操作</th>
</tr>
<% @books.each do |book| %>
<tr>
<td><%= book.title %></td>
<td><%= book.text %></td>
<td>
<%= link_to "显示", book_path(book) %><span style="margin-left: 5px">|</span>
<%= link_to "编辑", edit_admin_book_path(book) %><span style="margin-left: 5px">|</span>
</td>
</tr>
<% end %>
</table>
3.3 Book的 delete
修改文件 app/controllers/admin/books_controller.rb
class Admin::BooksController < ApplicationController
......
* def update
* end
def destroy
@book = Book.find(params[:id])
@book.destroy
redirect_to admin_books_path
end
* private
......
end
修改文件 app/views/admin/books/index.html.erb
删除动作直接在后台首页执行,需要再次确认删除提示
* <%= link_to "编辑", edit_admin_book_path(book) %><span style="margin-left: 5px">|</span>
<%= link_to "删除", admin_book_path(book),
method: :delete, data: { confirm: "确定删除本书?"} %>
* </td>
* </tr>
* <% end %>
</table>
4、管理员可以登录后台系统
4.1 users 数据表增加 is_admin 字段
终端执行
rails g migration add_is_admin_to_user
配置文件 db/migrate/xxxx一堆数字xxxx_add_is_admin_to_user.rb
class AddIsAdminToUser < ActiveRecord::Migration[5.1]
* def change
add_column :users, :is_admin, :boolean, deafault: false
* end
end
4.2 进入金手指 rails c ,手动配置管理员
rails c
u = User.first
u.is_admin = true
u.save
exit
4.3 加入管理员验证机制
修改文件 app/controllers/admin/books_controller.rb
class Admin::BooksController < ApplicationController
before_action :admin_required
* def index
end
修改文件 app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
* protect_from_forgery with: :exception
def admin_required
if !current_user.present?
redirect_to '/' , alert: "你不是管理员"
#在 rails5.1环境中,消息提示未出现,之后解决
end
end
end
4.4 建立后台布局( layout)
修改文件 app/controllers/admin/books_controller.rb
class Admin::BooksController < ApplicationController
layout "admin"
* before_action :admin_required
.....
end
终端执行
touch app/views/layouts/admin.html.erb
#建立后台布局文件
配置文件 app/views/layouts/admin.html.erb
<!DOCTYPE html>
<html>
<head>
<title>图书馆 后台</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">
<%= render "common/navbar" %>
<div class="row">
<div class="col-md-2">
<ul class="nav nav-pills nav-stacked" style="max-width: 300px;">
<li> <%= link_to("图书", admin_books_path) %> </li>
</ul>
</div>
<div class="col-md-10">
<%= yield %>
</div>
</div>
</div>
</body>
</html>
git add .
git commit -m "only admin can access backend panel"
5、图书状态“上架与下架”一键更改
5.1 路由配置
设定一键切换图书状态的路由
修改文件 config/routes.rb
Rails.application.routes.draw do
.......
* resources :books
* namespace :admin do
resources :books do
collection do
post "book_update"
end
end
* end
end
5.2 books 数据表增加 book_state字段
终端执行
#添加图书状态字段
rails g migration add_book_state_to_book
#添加图书库存字段
rails g migration add_book_stock_to_book
配置文件 db/migrate/xxxx一堆数字xxxx_add_book_state_to_book.rb
class AddBookStateToBook < ActiveRecord::Migration[5.1]
* def change
add_column :books, :book_state, :string
* end
end
配置文件 db/migrate/xxxx一堆数字xxxx_add_book_stock_to_book.rb
class AddBookStockToBook < ActiveRecord::Migration[5.1]
* def change
add_column :books, :book_stock, :integer
* end
end
5.3 Controller 设置图书状态切换方法
修改文件 app/controllers/admin/books_controller.rb
* def destroy
def book_update
@book = Book.find(params[:id]) #接受view层传递的id参数
if @book.book_state == "上架" #图书状态如果为"上架"字符串
@book.update(book_state: "下架") #更新为"下架"字符串状态
flash[:error] = "下架成功"
else #图书状态为非"上架"字符串
@book.update(book_state: "上架") #更新为"上架"字符串状态
flash[:error] = "上架成功"
end
redirect_to admin_books_path
end
* private
* def book_params
params.require(:book).permit(:title, :text, :book_stock, :book_state)
* end
end
5.4 View 层, 配置 book_state 代码
修改文件 app/views/admin/books/index.html.erb
<h1>图书馆后台</h1>
+ <%=flash[:error]%>
+ <%=flash[:waning]%>
+ <%=flash[:notice]%>
<p>
+ <%= link_to "新书上架", new_admin_book_path %>
</p>
<table class="table table-bordered table-hover">
<tr class="text-info">
<th>书名</th>
<th>简介</th>
+ <th>库存</th>
+ <th>状态</th>
<th>操作</th>
</tr>
<% @books.each do |book| %>
<tr class="text-info">
<td><%= book.title %></td>
<td><%= book.text %></td>
+ <td><%= book.book_stock %></td>
+ <td><%= link_to("#{book.book_state}", book_update_admin_books_path(:id => book.id),
method: :post , :class => "btn btn-xs btn-default") %></td>
6、借书单实作
6.1 配置路由
设定借书单、借书、还书的路由。
config/routes.rb
* namespace :admin do
.....
# 本页代码最下方
resources :borrows #借书单
resources :books do
member do
post :add_to_borrow #借书
post :return_book #还书
end
end
6.2 建立 borrows 与 borrow_items 数据表
终端执行
rails g migration create_borrows
rails g migration create_borrow_items
配置文件 db/migrate/xxxx一堆数字xxxx_borrow.rb
class CreateBorrows < ActiveRecord::Migration[5.1]
* def change
* create_table :borrows do |t|
t.timestamps
* end
* end
end
配置文件 db/migrate/xxxx一堆数字xxxx_borrow_items.rb
class CreateBorrowItems < ActiveRecord::Migration[5.1]
* def change
* create_table :borrow_items do |t|
t.integer :borrow_id
t.integer :book_id
t.integer :quantity, default: 1
t.timestamps
* end
* end
end
6.3 建立 borrow 与 borrow_item 的模型
终端执行
touch app/models/borrow.rb
touch app/models/borrow_item.rb
配置文件 app/models/borrow.rb
class Borrow < ApplicationRecord
has_many :borrow_items
has_many :books, through: :borrow_items, source: :book
def add_book_to_borrow(book)
bi = borrow_items.build
bi.book = book
bi.quantity = 1
bi.save
end
end
配置文件 app/models/borrow_item.rb
class BorrowItem < ApplicationRecord
belongs_to :book
belongs_to :borrow
end
6.4 Model 层,测试借书动作
终端进入 rails c (后台金手指)
rails c
book = Book.first
Borrow.create
borrow = Borrow.first
borrow.add_book_to_borrow(book)
borrow.borrow_items
6.5 Controller 层,配置文件
终端执行
touch app/controllers/borrows_controller.rb
配置文件 app/controllers/borrows_controller.rb
class BorrowsController < ApplicationController
end
修改文件 app/controllers/books_controller.rb
class BooksController < ApplicationController
......
* def show
* end
def add_to_borrow #加入借书单
@book = Book.find(params[:id])
if !current_borrow.books.include?(@book)
current_borrow.add_book_to_borrow(@book)
@book.book_stock = @book.book_stock - 1
@book.save
flash[:notice] = "成功将 #{@book.title} 加入借书单"
else
flash[:waning] = "你的借书单已有本书"
end
redirect_to books_path
end
def return_book #还书
@borrow_item = BorrowItem.find(params[:id])
if @borrow_item.destroy
@borrow_item.book.book_stock = @borrow_item.book.book_stock + 1
@borrow_item.book.save
redirect_to borrows_path
flash[:notice] = "还书成功"
else
flash[:error] = "还书失败"
end
end
*end
6.5 View 层,配置借书、还书、借书单
修改文件 app/views/common/_navbar.html.erb ,导航栏加入借书单按钮
* <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
* <ul class="nav navbar-nav navbar-right">
<li>
<%= link_to borrows_path do %>
借书单 <i class="fa fa-shopping-borrow"> </i> ( <%= current_borrow.books.count %> )
<% end %>
</li>
* <% if !current_user %>
* <li><%= link_to("注册", new_user_path) %> </li>
终端执行
mkdir app/views/borrows
touch app/views/borrows/index.html.erb
配置文件 app/views/borrows/index.html.erb
<h1>借书单</h1>
<table class="table table-bordered table-hover">
<tr class="text-info">
<th>书名</th>
<th>简介</th>
<th>库存</th>
<th>操作</th>
</tr>
<% current_borrow.borrow_items.each do |borrow_item| %>
<tr>
<th><%= borrow_item.book.title %></th>
<th><%= borrow_item.book.text %></th>
<th><%= borrow_item.book.book_stock %></th>
<th><%= link_to '还书', return_book_book_path(:id => borrow_item.id),
:method => :post, :class => "btn btn-primary btn-lg btn-danger" %></th>
</tr>
<% end %>
</table>
修改文件 app/views/books/index.html.erb ,加入借书按钮
* <td><%= book.book_stock %></td>
* <td>
* <%= link_to '显示', book_path(book) %>
<%if current_user%>
<span style="margin-left: 5px;">|</span>
<%= link_to '借阅', add_to_borrow_book_path(:id => book.id), :method => :post, :class => "btn btn-primary btn-lg btn-danger" %>
<%end%>
* </td>