rails 入门笔记
rails 入门笔记
官方教程链接
学习上述教程的过程中加入了一些相关知识,也缩减了一些我暂时还看不懂的东西
环境
windows
ruby2.7
rails6.1
新建项目
打开cmd
,输入rails new 项目名
例如:
$ rails new myblog
生成一个名为myblog的rails项目。
进入myblog
目录,在cmd
中输入rails server
,即可启动rails服务器。默认在3000
端口,用浏览器访问localhost:3000
即可看到rails的欢迎界面。
如果你不要小心在运行服务器的时候把cmd关掉了,再启动cmd想要运行rails serer的时候,它会提醒你server已经在其他进程中运行。这个时候,你需要杀死之前运行的服务器进程,方法如下:
- 在
/tmo/pids/server.pid
中找到server的运行编号,下面以编号9476为例。- 在cmd中查看这个命令编号,确认是否是rails
- 命令为:
tasklist | findstr 9476
- 在cmd中杀死这个进程:
taskkill /pid 9476 -t -f
新建控制器
在cmd
中输入:
$ rails generate controller Welcome index
创建一个包含“index”动作的“Welcome”控制器。
MVC结构:model-viewer-controller模式(模型-视图-控制器),关于这三部分的具体讲解在本教程中将随着web开发的进行而深入。
成功创建控制器后,你将看到rails为你新建的文件:
$ 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
invoke test_unit
invoke assets
invoke scss
create app/assets/stylesheets/welcome.scss
控制器(controller)位于app/controllers/welcome_controller.rb
文件,
视图(view)位于app/views/welcome/index.html.erb
文件,打开这个文件,添加
<h1>Hello, Rails!</h1>
即可在页面中显示Hello, Rails!
。我们注意到,这个文件的语法规则和html
是类似的。你可以在localhost:3000/welcome/index
找到这个页面。
我们也可以新建一个不带任何动作的控制器:
$ rails generate conrtroller Articles
打开app/controllers/welcome_controller.rb
和app/controllers/articles_controller.rb
,你会看到二者的不同之处。
打开app/controllers/articles_controller.rb
,其中代码为:
class ArticlesController < ApplicationController
end
而app/controllers/welcome_controller.rb
中则是
class WelcomeController < ApplicationController
def index
end
end
从上述代码中,我们可以发现,控制器本质上是一个继承自
ApplicationController
的类。
那么我们如何从空的控制器中新建方法呢?
首先,我们在这个控制器中定义一个new
方法:
class ArticlesController < ApplicationController
def new
end
end
然后为这个方法新建页面,在app/views/
下新建articles/new.html.erb
,就可以了。访问 http://localhost:3000/articles/new
即可看到新建的页面。
如果发生报错
Rails: Webpacker::Manifest::MissingEntryError in Home#index
,那么请:
- 检查node.js版本,跟新到最新稳定版本
- 检查webpacker安装,在命令行输入
bundle exec rake webpacker:install
即可
设置应用主页
代开config/routes.rb
文件,这是路由的配置文件。可以看到里面已经生成了一些DSL代码(Domain-Specific Language,领域专属语言):
Rails.application.routes.draw do
get 'welcome/index'
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end
其中get 'welcome/index'
表示应用中有一个get
方法,地址是localhost:3000/welcome/index
。
我们可以添加一行root 'welcome#index'
,将welcome
中的index
页面设置成整个应用的主页,此时,运行服务器,访问``localhost:3000看到是将
welcome/index`页面。
创建资源
资源是一个术语,表示一系列类似对象的集合,如文章、人或动物。
在config/routes.rb
中添加article
资源。
Rails.application.routes.draw do
get 'welcome/index'
resources :articles
root 'welcome#index'
end
创建表单
在app/views/articles/new.html.erb
中添加:
<%= form_for :article, url: articles_path do |f| %>
<p>
<%= f.label :title %><br>
<%= f.text_field :title %>
</p>
<p>
<%= f.label :text %><br>
<%= f.text_area :text %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
刷新页面,即可看到表单。
调用
form_for
辅助方法时,需要为表单传递一个标识对象作为参数,这里是:article
符号。这个符号告诉form_for
辅助方法表单用于处理哪个对象。在form_for
辅助方法的块中,f
表示FormBuilder
对象,用于创建两个标签和两个文本字段,分别用于添加文章的标题和正文。最后在f
对象上调用submit
方法来为表单创建提交按钮。
值得注意的是url这部分,url是传入的articles的地址,这意味着会将其与articles的poat方法相连。
利用rail routes查看一下路由管理的全部的地址,可以看到以下一项
articles GET /articles(.:format) articles#index
POST /articles(.:format) articles#create
在rails中,创建模型的时候,已经为我们创建好了基本的CRUD 动作(
index
,show
,new
,edit
,create
,update
和destroy
),我们可以从路由中找到对应地址。
因此,事实上是与create
动作绑定的。
接下来,我们在articles
的controller中定义一个create
动作,这样表单就可以与创建文章这个动作绑定了。
此时,我们运行服务器,提交表单,看到:
#<ActionController::Parameters {"title"=>"12", "text"=>"1"} permitted: false>
可以看到,虽然正确传递了参数,但是没有构建模型,存储数据。
创建模型
创建模型命令如下:
$ rails generate model Article title:string text:text
此时,我们创建了一个名字为Article
的模型,这个模型由两个属性:string
类型的title
和text
类型的text
同时,我们会看到rails创建了db/migrate/20210914114124_create_articles.rb
打开这个文件,会看到:
class CreateArticles < ActiveRecord::Migration[6.1]
def change
create_table :articles do |t|
t.string :title
t.text :text
t.timestamps
end
end
end
db/migrate中的文件是帮助记录数据库迁移中执行的具体操作,文件名以时间戳命名,方便撤销。
执行rails db:migrate
,进行数据库的迁移,这条命令执行后,可以看到:
== 20210914114124 CreateArticles: migrating ===================================
-- create_table(:articles)
-> 0.0022s
== 20210914114124 CreateArticles: migrated (0.0031s) ==========================
此时,我们才真正建立了articles
表,并且表中有Article
模型。
然后,我们回过头来修改create
方法,让它能够将我们输入的数据保存起来。
class ArticlesController < ApplicationController
def new
end
def create
@article = Article.new(params[:article]) # 新建一个Article模型的实例,接收传入的参数
# 注意Ruby中的类名必须是大写字母开头
@article.save # 保存这个实例
redirect_to @article # 重定向至show动作
end
end
运行服务器,提交表单,发现报错:
ActiveModel::ForbiddenAttributesError in ArticlesController#create
ActiveModel::ForbiddenAttributesError
报错源码:
@article = Article.new(params[:article]) # 新建一个Article模型的实例,接收传入的参数
这是由于我们没有使用“健壮参数”(strong parameter),“健壮参数”原则要求我们告诉rails哪些参数允许咋控制器中使用。这是rails安全性设置之一,为了防止恶意攻击数据库。
所以,我们要将上面这句源码修改为:
@article = Article.new(params.require(:article).permit(:title, :text))
permit方法中的字段为合法字段,用户传入的其他字段将被阻拦。
同时,为了禁止从外部调用这个方法,通常还要把它设置为 private
。
class ArticlesController < ApplicationController
def new
end
def create
@article = Article.new(article_params) # 新建一个Article模型的实例,接收传入的参数
# 注意Ruby中的类名必须是大写字母开头
@article.save # 保存这个实例
redirect_to @article # 重定向至show动作
end
private
def article_params
params.require(:article).permit(:title, :text)
end
end
需要注意的是,在ruby中private修饰的是自private之下的所有方法!也就是说,如果你将create方法放到article_params之后,那么create也将变成私有方法。然而,标准的 CRUD 动作(
index
,show
,new
,edit
,create
,update
和destroy
),都必须是公开方法,不可以放在private之后。
在路由中($ rails routes
)发现,关于article的show方法是:
article GET /articles/:id(.:format) articles#show
那么我们可以编写show方法,来展示文章。
def show
@article = Article.find(params[:id]) # 选取对应id的文章
end
新建app/views/articles/show.html.erb
,写入
<p>
<strong>Title:</strong>
<%= @article.title %>
</p>
<p>
<strong>Text:</strong>
<%= @article.text %>
</p>
功能完善
展示所有文章列表
从rails routes
中找到index
方法
articles GET /articles(.:format) articles#index
在articles_controller.rb
中写入:
def index
@articles = Article.all
end
在index.html.rb
中写入:
<h1>Listing articles</h1>
<table>
<tr>
<th>Title</th>
<th>Text</th>
</tr>
<% @articles.each do |article| %>
<tr>
<td><%= article.title %></td>
<td><%= article.text %></td>
<td><%= link_to 'Show', article_path(article) %></td>
</tr>
<% end %>
</table>
link_to
方法是 Rails 内置的视图辅助方法之一,用于创建基于链接文本和地址的超链接。在这里地址指的是文章列表页面的路径。
添加链接
在welcome页中添加向文章列表的链接。
在welcome/index.html.erb
中添加:
<%= link_to 'My Blog', controller: 'articles' %>
请自行联系添加一些在各个页面中添加一些跳转和返回链接。
链接到当前控制器的动作时不需要指定
:controller
选项,因为 Rails 默认使用当前控制器.
添加验证
打开app/models/article.rb
,修改为:
class Article < ApplicationRecord
validates :title, presence: true, length: { minimum: 5 }
end
用于检查title是否存在、长度不少于5字符。
修改controller中的方法:
if @article.save # 保存这个实例
redirect_to @article # 重定向至show动作
else
render 'new'
end
将new.html.erb改为:
<%= form_for :article, url: articles_path do |f| %>
<% if @article.errors.any? %>
<div id="error_explanation">
<h2>
<%= pluralize(@article.errors.count, "error") %> prohibited
this article from being saved:
</h2>
<ul>
<% @article.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<p>
<%= f.label :title %><br>
<%= f.text_field :title %>
</p>
<p>
<%= f.label :text %><br>
<%= f.text_area :text %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
<%= link_to 'Back', articles_path %>
模型之间的关联
创建关联模型
在rails中模块之间可以相互关联,允许一个模型包含若干个子模型,比如对于一个文章可以含有若干条评论,则model article可以包含 model comments。
子模块创建方式:在正常创建命令后加父模块名:references
,即
$ bin/rails generate model Comment commenter:string body:text article:references
然后,进行migrate
就好了。
打开创建comment
对应的migrate
文件,发现只是比创建article
多了一行t.references :article, null: false, foreign_key: true
class CreateComments < ActiveRecord::Migration[6.1]
def change
create_table :comments do |t|
t.string :commenter
t.text :body
t.references :article, null: false, foreign_key: true
t.timestamps
end
end
end
如果忘记加关联,想要重新生成数据表怎么办?这里介绍一下通过修改migration文件来进行数据库表单修改操作。
- 如果还没进行migrate操作,可以打开
/db/migrate/
文件夹,找到对应的migrate文件,增加t.references :article, foreign_key: true
- 如果已经进行了
migrate
操作,那么可以在create_tabble
前加一行drop_table :comments
,即可删除此前创建的表,并重新创建最后,不要忘记进行
migrate
操作
接下来需要修改app/models/comment.rb
:
class Comment < ApplicationRecord
belongs_to :article
end
表示一条评论属于一篇文章。
修改app/model/article.rb
:
class Article < ApplicationRecord
has_many :comments
validates :title, presence: true, length: {minimum: 5}
end
表示一篇文章可以有多条评论。
修改控制器
创建控制器Comments
,并添加:
class CommentsController < ApplicationController
def create
@article = Article.find(params[:article_id])
@comment = @article.comments.create(comment_params)
redirect_to article_path(@article)
end
private
def comment_params
params.require(:comment).permit(:commenter, :body)
end
end
修改app/views/articles/show.html.erb
:
<p>
<strong>Title:</strong>
<%= @article.title %>
</p>
<p>
<strong>Text:</strong>
<%= @article.text %>
</p>
<h2>Comments</h2>
<% @article.comments.each do |comment| %>
<p>
<strong>Commenter:</strong>
<%= comment.commenter %>
</p>
<p>
<strong>Comment:</strong>
<%= comment.body %>
</p>
<% end %>
<h2>Add a comment:</h2>
<%= form_for([@article, @article.comments.build]) do |f| %>
<p>
<%= f.label :commenter %><br>
<%= f.text_field :commenter %>
</p>
<p>
<%= f.label :body %><br>
<%= f.text_area :body %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
<%= link_to 'Edit', edit_article_path(@article) %> |
<%= link_to 'Back', articles_path %>
即可进行评论的添加与查看。
重构
我们发现app/views/articles/show.html.erb
中的内容过多,而且其中很多是comment
的内容,因此,我们可以将关于comment
的内容取出:
创建app/views/comments/_comment.html.erb
:
<p>
<strong>Commenter:</strong>
<%= comment.commenter %>
</p>
<p>
<strong>Comment:</strong>
<%= comment.body %>
</p>
创建app/views/comments/_form.html.erb
:
<%= form_for([@article, @article.comments.build]) do |f| %>
<p>
<%= f.label :commenter %><br>
<%= f.text_field :commenter %>
</p>
<p>
<%= f.label :body %><br>
<%= f.text_area :body %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
修改app/views/articles/show.html.erb
:
<p>
<strong>Title:</strong>
<%= @article.title %>
</p>
<p>
<strong>Text:</strong>
<%= @article.text %>
</p>
<h2>Comments</h2>
<%= render @article.comments %>
<h2>Add a comment:</h2>
<%= render 'comments/form' %>
<%= link_to 'Edit', edit_article_path(@article) %> |
<%= link_to 'Back', articles_path %>
关联模型的删除
删除评论
在app/views/comments/_comment.html.erb
中添加;
<p>
<%= link_to 'Destroy Comment', [comment.article, comment],
method: :delete,
data: { confirm: 'Are you sure?' } %>
</p>
在comment_controller.rb
中添加destory
方法:
def destroy
@article = Article.find(params[:article_id])
@comment = @article.comments.find(params[:id])
@comment.destroy
redirect_to article_path(@article)
end
删除文章同时删除相关评论
在app/model/article.rb
中修改:
class Article < ApplicationRecord
has_many :comments, dependent: :destroy
validates :title, presence: true,
length: { minimum: 5 }
end
基本身份验证
仅介绍一种基本方法:
在controller中添加如下代码:
class ArticlesController < ApplicationController
http_basic_authenticate_with name: "dhh", password: "secret", except: [:index, :show]
def index
@articles = Article.all
end
# 为了行文简洁,省略以下内容
或
class CommentsController < ApplicationController
http_basic_authenticate_with name: "dhh", password: "secret", only: :destroy
def create
@article = Article.find(params[:article_id])
# ...
end
# 为了行文简洁,省略以下内容
进阶指南
请不要独立完成一切!!!请不要独立完成一切!!
rails可以帮你搞定大多数问题,请不要独自完成一切。
- Ruby on Rails 指南
- Ruby on Rails 教程
- Ruby on Rails 邮件列表
- irc.freenode.net 中的 #rubyonrails 频道