Rails第一个应用 #

一、创建项目 #

1.1 生成项目 #

bash
# 创建新的Rails项目
rails new blog

# 进入项目目录
cd blog

# 创建数据库
rails db:create

1.2 项目结构概览 #

text
blog/
├── app/
│   ├── controllers/      # 控制器
│   │   └── application_controller.rb
│   ├── models/           # 模型
│   │   └── application_record.rb
│   ├── views/            # 视图
│   │   └── layouts/
│   │       └── application.html.erb
│   ├── helpers/          # 辅助方法
│   ├── javascript/       # JavaScript
│   └── assets/           # 静态资源
├── config/
│   ├── routes.rb         # 路由配置
│   ├── database.yml      # 数据库配置
│   └── environments/     # 环境配置
├── db/
│   ├── migrate/          # 迁移文件
│   ├── schema.rb         # 数据库结构
│   └── seeds.rb          # 种子数据
├── public/               # 公共文件
├── Gemfile               # Gem依赖
└── Gemfile.lock          # 锁定版本

二、Hello World #

2.1 创建控制器 #

bash
# 生成控制器
rails generate controller Welcome index

# 输出:
# create  app/controllers/welcome_controller.rb
# route   get 'welcome/index'
# invoke  erb
# create    app/views/welcome
# create    app/views/welcome/index.html.erb
# invoke  test_unit
# create    test/controllers/welcome_controller_test.rb
# invoke  helper
# create    app/helpers/welcome_helper.rb

2.2 控制器代码 #

ruby
# app/controllers/welcome_controller.rb
class WelcomeController < ApplicationController
  def index
  end
end

2.3 视图代码 #

erb
<!-- app/views/welcome/index.html.erb -->
<h1>Welcome to Rails</h1>
<p>Hello, World!</p>
<p>现在是:<%= Time.now.strftime('%Y-%m-%d %H:%M:%S') %></p>

2.4 设置根路由 #

ruby
# config/routes.rb
Rails.application.routes.draw do
  root 'welcome#index'
  
  # 也可以这样写
  # get 'welcome/index'
  # root to: 'welcome#index'
end

2.5 启动应用 #

bash
# 启动服务器
rails server

# 访问 http://localhost:3000

三、使用脚手架 #

3.1 什么是脚手架 #

脚手架(Scaffold)是Rails的代码生成器,可以快速创建完整的CRUD(创建、读取、更新、删除)功能。

3.2 创建Article脚手架 #

bash
# 生成Article脚手架
rails generate scaffold Article title:string body:text

# 输出:
# invoke  active_record
# create    db/migrate/20240328000001_create_articles.rb
# create    app/models/article.rb
# invoke    test_unit
# create      test/models/article_test.rb
# invoke  resource_route
# route    resources :articles
# invoke  scaffold_controller
# create    app/controllers/articles_controller.rb
# invoke    erb
# create      app/views/articles
# create      app/views/articles/index.html.erb
# create      app/views/articles/edit.html.erb
# create      app/views/articles/show.html.erb
# create      app/views/articles/new.html.erb
# create      app/views/articles/_article.html.erb
# invoke    test_unit
# create      test/controllers/articles_controller_test.rb
# invoke    helper
# create      app/helpers/articles_helper.rb

3.3 运行迁移 #

bash
# 执行数据库迁移
rails db:migrate

# 输出:
# == 20240328000001 CreateArticles: migrating ===================================
# -- create_table(:articles)
#    -> 0.0018s
# == 20240328000001 CreateArticles: migrated (0.0019s) ==========================

3.4 生成的文件 #

迁移文件:

ruby
# db/migrate/20240328000001_create_articles.rb
class CreateArticles < ActiveRecord::Migration[7.1]
  def change
    create_table :articles do |t|
      t.string :title
      t.text :body

      t.timestamps
    end
  end
end

模型文件:

ruby
# app/models/article.rb
class Article < ApplicationRecord
end

控制器文件:

ruby
# app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
  before_action :set_article, only: %i[ show edit update destroy ]

  def index
    @articles = Article.all
  end

  def show
  end

  def new
    @article = Article.new
  end

  def edit
  end

  def create
    @article = Article.new(article_params)

    respond_to do |format|
      if @article.save
        format.html { redirect_to article_url(@article), notice: "Article was successfully created." }
        format.json { render :show, status: :created, location: @article }
      else
        format.html { render :new, status: :unprocessable_entity }
        format.json { render json: @article.errors, status: :unprocessable_entity }
      end
    end
  end

  def update
    respond_to do |format|
      if @article.update(article_params)
        format.html { redirect_to article_url(@article), notice: "Article was successfully updated." }
        format.json { render :show, status: :ok, location: @article }
      else
        format.html { render :edit, status: :unprocessable_entity }
        format.json { render json: @article.errors, status: :unprocessable_entity }
      end
    end
  end

  def destroy
    @article.destroy!

    respond_to do |format|
      format.html { redirect_to articles_url, notice: "Article was successfully destroyed." }
      format.json { head :no_content }
    end
  end

  private

  def set_article
    @article = Article.find(params[:id])
  end

  def article_params
    params.require(:article).permit(:title, :body)
  end
end

路由配置:

ruby
# config/routes.rb
Rails.application.routes.draw do
  resources :articles
  root 'welcome#index'
end

3.5 视图文件 #

列表页面:

erb
<!-- app/views/articles/index.html.erb -->
<p style="color: green"><%= notice %></p>

<h1>Articles</h1>

<div id="articles">
  <% @articles.each do |article| %>
    <%= render article %>
    <p>
      <%= link_to "Show this article", article %>
    </p>
  <% end %>
</div>

<%= link_to "New article", new_article_path %>

详情页面:

erb
<!-- app/views/articles/show.html.erb -->
<p style="color: green"><%= notice %></p>

<%= render @article %>

<div>
  <%= link_to "Edit this article", edit_article_path(@article) %> |
  <%= link_to "Back to articles", articles_path %>

  <%= button_to "Destroy this article", @article, method: :delete %>
</div>

表单页面:

erb
<!-- app/views/articles/_form.html.erb -->
<%= form_with(model: article) do |form| %>
  <% if article.errors.any? %>
    <div style="color: red">
      <h2><%= pluralize(article.errors.count, "error") %> prohibited this article from being saved:</h2>

      <ul>
        <% article.errors.each do |error| %>
          <li><%= error.full_message %></li>
        <% end %>
      </ul>
    </div>
  <% end %>

  <div>
    <%= form.label :title, style: "display: block" %>
    <%= form.text_field :title %>
  </div>

  <div>
    <%= form.label :body, style: "display: block" %>
    <%= form.text_area :body %>
  </div>

  <div>
    <%= form.submit %>
  </div>
<% end %>

四、添加数据验证 #

4.1 模型验证 #

ruby
# app/models/article.rb
class Article < ApplicationRecord
  validates :title, presence: true,
                    length: { minimum: 5, maximum: 100 }
  validates :body, presence: true,
                   length: { minimum: 10 }
end

4.2 测试验证 #

bash
# 启动Rails控制台
rails console

# 创建无效的文章
article = Article.new(title: "Hi", body: "Too short")
article.valid?
# => false

article.errors.full_messages
# => ["Title is too short (minimum is 5 characters)", "Body is too short (minimum is 10 characters)"]

# 创建有效的文章
article = Article.new(title: "My First Article", body: "This is the body of my first article.")
article.valid?
# => true

article.save
# => true

五、添加评论功能 #

5.1 生成Comment模型 #

bash
# 生成Comment模型
rails generate model Comment commenter:string body:text article:references

# 执行迁移
rails db:migrate

5.2 建立关联关系 #

ruby
# app/models/article.rb
class Article < ApplicationRecord
  has_many :comments, dependent: :destroy
  
  validates :title, presence: true,
                    length: { minimum: 5, maximum: 100 }
  validates :body, presence: true,
                   length: { minimum: 10 }
end

# app/models/comment.rb
class Comment < ApplicationRecord
  belongs_to :article
  
  validates :commenter, presence: true
  validates :body, presence: true
end

5.3 添加评论路由 #

ruby
# config/routes.rb
Rails.application.routes.draw do
  resources :articles do
    resources :comments
  end
  root 'welcome#index'
end

5.4 生成评论控制器 #

bash
rails generate controller Comments

5.5 评论控制器 #

ruby
# app/controllers/comments_controller.rb
class CommentsController < ApplicationController
  def create
    @article = Article.find(params[:article_id])
    @comment = @article.comments.create(comment_params)
    redirect_to article_path(@article)
  end

  def destroy
    @article = Article.find(params[:article_id])
    @comment = @article.comments.find(params[:id])
    @comment.destroy
    redirect_to article_path(@article), status: :see_other
  end

  private

  def comment_params
    params.require(:comment).permit(:commenter, :body)
  end
end

5.6 在文章页面显示评论 #

erb
<!-- app/views/articles/show.html.erb -->
<p style="color: green"><%= notice %></p>

<%= render @article %>

<h2>Comments</h2>
<div id="comments">
  <% @article.comments.each do |comment| %>
    <p>
      <strong>Commenter:</strong>
      <%= comment.commenter %>
    </p>
    <p>
      <strong>Comment:</strong>
      <%= comment.body %>
    </p>
    <p>
      <%= link_to "Destroy Comment", [@article, comment],
                  data: {
                    turbo_method: :delete,
                    turbo_confirm: "Are you sure?"
                  } %>
    </p>
  <% end %>
</div>

<h2>Add a comment:</h2>
<%= form_with(model: [@article, @article.comments.build]) do |form| %>
  <p>
    <%= form.label :commenter %><br>
    <%= form.text_field :commenter %>
  </p>
  <p>
    <%= form.label :body %><br>
    <%= form.text_area :body %>
  </p>
  <p>
    <%= form.submit %>
  </p>
<% end %>

<div>
  <%= link_to "Edit this article", edit_article_path(@article) %> |
  <%= link_to "Back to articles", articles_path %>
  <%= button_to "Destroy this article", @article, method: :delete %>
</div>

六、添加样式 #

6.1 使用CSS #

css
/* app/assets/stylesheets/application.css */
body {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  margin: 0;
  padding: 20px;
  background-color: #f5f5f5;
}

h1, h2, h3 {
  color: #333;
}

.article {
  background: white;
  padding: 20px;
  margin-bottom: 20px;
  border-radius: 8px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

.comment {
  background: #f9f9f9;
  padding: 15px;
  margin: 10px 0;
  border-left: 3px solid #007bff;
}

form {
  background: white;
  padding: 20px;
  border-radius: 8px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

input[type="text"],
textarea {
  width: 100%;
  padding: 10px;
  margin: 5px 0;
  border: 1px solid #ddd;
  border-radius: 4px;
}

input[type="submit"] {
  background: #007bff;
  color: white;
  border: none;
  padding: 10px 20px;
  border-radius: 4px;
  cursor: pointer;
}

input[type="submit"]:hover {
  background: #0056b3;
}

a {
  color: #007bff;
  text-decoration: none;
}

a:hover {
  text-decoration: underline;
}

.notice {
  background: #d4edda;
  color: #155724;
  padding: 10px;
  border-radius: 4px;
  margin-bottom: 20px;
}

.alert {
  background: #f8d7da;
  color: #721c24;
  padding: 10px;
  border-radius: 4px;
  margin-bottom: 20px;
}

七、常用Rails命令 #

7.1 生成器命令 #

命令 说明
rails generate model 生成模型
rails generate controller 生成控制器
rails generate scaffold 生成脚手架
rails generate migration 生成迁移
rails generate resource 生成资源

7.2 数据库命令 #

命令 说明
rails db:create 创建数据库
rails db:drop 删除数据库
rails db:migrate 执行迁移
rails db:rollback 回滚迁移
rails db:seed 填充数据
rails db:reset 重置数据库

7.3 服务器命令 #

命令 说明
rails server 启动服务器
rails console 启动控制台
rails runner 执行Ruby代码
rails routes 查看路由
rails stats 查看统计信息

7.4 撤销生成 #

bash
# 撤销脚手架
rails destroy scaffold Article

# 撤销控制器
rails destroy controller Welcome

# 撤销模型
rails destroy model Comment

# 回滚迁移
rails db:rollback

八、项目测试 #

8.1 启动服务器 #

bash
rails server

8.2 访问页面 #

URL 说明
http://localhost:3000 首页
http://localhost:3000/articles 文章列表
http://localhost:3000/articles/new 新建文章
http://localhost:3000/articles/1 查看文章
http://localhost:3000/articles/1/edit 编辑文章

8.3 测试CRUD操作 #

  1. 访问文章列表页面
  2. 点击"New article"创建新文章
  3. 填写标题和内容,点击提交
  4. 查看文章详情
  5. 添加评论
  6. 编辑文章
  7. 删除文章

九、调试技巧 #

9.1 使用Rails控制台 #

bash
# 启动控制台
rails console

# 查询数据
Article.all
Article.first
Article.find_by(title: "My First Article")

# 创建数据
Article.create(title: "Test", body: "Test body")

# 更新数据
article = Article.first
article.update(title: "New Title")

# 删除数据
article.destroy

9.2 使用debug方法 #

erb
<!-- 在视图中调试 -->
<%= debug(params) %>
<%= debug(@article) %>

9.3 使用byebug #

ruby
# 在控制器中设置断点
def show
  @article = Article.find(params[:id])
  byebug  # 程序会在这里暂停
end

9.4 查看日志 #

bash
# 查看开发日志
tail -f log/development.log

# 查看SQL查询
# 在日志中可以看到所有执行的SQL语句

十、总结 #

10.1 核心要点 #

要点 说明
创建项目 rails new app_name
生成脚手架 rails generate scaffold Model field:type
数据库迁移 rails db:migrate
启动服务器 rails server
路由配置 config/routes.rb
模型验证 validates 方法

10.2 下一步 #

现在你已经创建了第一个Rails应用,接下来让我们学习 项目结构,深入了解Rails项目的组织方式!

最后更新:2026-03-28