Rails 实战——图书管理系统——图书后台

本教程详述如何使用Rails构建图书管理后台,包括配置路由、创建Book数据表及模型、实现CRUD操作、管理员登录验证、图书状态切换及借书单功能。通过一步步指导,让你掌握Rails后台开发技巧。
摘要由CSDN通过智能技术生成

目标

建立图书管理后台,实现图书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>

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值