Rails model
创建模型(Model)
在MVC开发中, model
指使用对应数据的Ruby类,也就是Models通过一个名为Active Record的Rails功能来和应用程序的数据库交互
要定义一个model,需要使用model generator:
bin/rails generate model Tweet title:string body:text
model
命名冲突
警告
需要注意, model
的名字不能和Rails的应用名字相同!!!
我最初不知道这个Rails不允许 model
名字(数据库连接)和应用名字相同,也是就是我的项目名设置为 tweet
,然后我尝试创建Model命名也是 Tweet
:
bin/rails generate model Tweet title:string body:text
此时报错显示 Tweet
名字是保留字或者已经在应用中使用了。Why?
通过 grep -R -i tweet *
可以看到在 config/application.rb
有一个冲突命名 module
:
config/application.rb
内部包含了和应用名相同的 module
配置require_relative "boot"
require "rails/all"
# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(*Rails.groups)
module Tweet
class Application < Rails::Application
# Initialize configuration defaults for originally generated Rails version.
config.load_defaults 7.1
# Please, add to the `ignore` list any other `lib` subdirectories that do
# not contain `.rb` files, or that should not be reloaded or eager loaded.
# Common ones are `templates`, `generators`, or `middleware`, for example.
config.autoload_lib(ignore: %w(assets tasks))
# Configuration for the application, engines, and railties goes here.
#
# These settings can be overridden in specific environments using the files
# in config/environments, which are processed later.
#
# config.time_zone = "Central Time (US & Canada)"
# config.eager_load_paths << Rails.root.join("extras")
end
end
原因是创建名为 tweet
的应用程序时,这个关键字 tweet
就是模块(module)命名保留,后续就不能作为模型(model)使用了。
为了避免这样的冲突,可以修改使用其他模型(model)名,或者采用更好的应用模块(module)命名。例如,在命名应用程序是,如果考虑到 tweet
会作为model命名(数据库表名),那么可以采用 tweets
作为应用名,既表示复数又能够避免冲突。
我重新构建了项目,命名项目名为 tweets
来重走一遍流程(毕竟刚开始工作量不多),后续吸取教训,在理解和遵循社区最佳实践基础上,合理命名。
在修正了应用名
tweets
之后,重新执行一次定义model的命令(同上):
bin/rails generate model Tweet title:string body:text
此时就能够正常运行,输出信息如下:
invoke active_record
create db/migrate/20240507031631_create_tweets.rb
create app/models/tweet.rb
invoke test_unit
create test/models/tweet_test.rb
create test/fixtures/tweets.yml
上面的输出信息中显示生成了两个文件:
db/migrate/20240507031631_create_tweets.rb
数据库迁移文件
app/models/tweet.rb
模型文件
数据库迁移
db/migrate/20240507031631_create_tweets.rb
是Ruby编写的数据库抽象:
class CreateTweets < ActiveRecord::Migration[7.1]
def change
create_table :tweets do |t|
t.string :title
t.text :body
t.timestamps
end
end
end
注意到,这里我虽然创建的是 Tweet
model,但是在数据库表中建立的却是 tweets
表
在这个
tweets
表中,默认创建了一个自增主键(auto-incrementing primary key)id
,会自动从id
1 开始增加创建表的部分,定义了
title
和body
两列,类型分别是string
和text
最后一行定义表是
t.timestamps
,这个方法实际上定义了两个附加列created_at
和updated_at
执行数据库迁移指令:
bin/rails db:migrate
输出信息显示了创建一个 tweets
表:
tweets
表== 20240507031631 CreateTweets: migrating =====================================
-- create_table(:tweets)
-> 0.0215s
== 20240507031631 CreateTweets: migrated (0.0216s) ============================
数据库交互
Rails提供了一个非常 神奇 的数据库交互方法,也就是控制台提供 irb
交互能够以对象方式操作数据库。下面是一个在数据库表 tweets
中插入一行记录并检查的交互过程
加载Rails控制台:
bin/rails console
此时提示:
Loading development environment (Rails 7.1.3.2)
irb(main):001>
输入以下命令,初始化一个Rails的
tweet
对象:
tweet = Tweet.new(title: "Hello Rails", body: "I am on Rails!")
此时会输出生成了对象,但是需要注意这个对象仅仅初始化,还没有真正保存到数据库
=>
#<Tweet:0x00000001108a64c8
...
irb(main):002>
输入保存命令:
tweet.save
保存成功的信息输出
TRANSACTION (0.1ms) begin transaction
Tweet Create (9.3ms) INSERT INTO "tweets" ("title", "body", "created_at", "updated_at") VALUES (?, ?, ?, ?) RETURNING "id" [["title", "Hello Rails"], ["body", "I am on Rails!"], ["created_at", "2024-05-07 07:52:58.316455"], ["updated_at", "2024-05-07 07:52:58.316455"]]
TRANSACTION (0.4ms) commit transaction
=> true
irb(main):003>
查看和打印非常简单,就是直接调用对象:
tweet
输出信息如下
=>
#<Tweet:0x00000001108a64c8
id: 1,
title: "Hello Rails",
body: "I am on Rails!",
created_at: Tue, 07 May 2024 07:52:58.316455000 UTC +00:00,
updated_at: Tue, 07 May 2024 07:52:58.316455000 UTC +00:00>
irb(main):004>
非常简单的方法,我们就得到了一个插入数据库的记录
可以通过以下命令来查找记录,这里举id为1的查询:
Tweet.find(1)
输出如下:
Tweet Load (1.0ms) SELECT "tweets".* FROM "tweets" WHERE "tweets"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
=>
#<Tweet:0x0000000110aca3d0
id: 1,
title: "Hello Rails",
body: "I am on Rails!",
created_at: Tue, 07 May 2024 07:52:58.316455000 UTC +00:00,
updated_at: Tue, 07 May 2024 07:52:58.316455000 UTC +00:00>
irb(main):005>
打印所有记录:
Tweet.all
数据库数据List
在完成了上文的数据库交互数据添加之后,对于WEB开发来说,如何在页面上展示数据(模拟用户发布tweet之后的页面显示),成为基础功能。简单的实现也是通过经典的MVC实现:
首先修订
app/controllers/tweets_controller.rb
控制器:
app/controllers/tweets_controller.rb
添加索引index动作以便从数据库提取所有tweetclass TweetsController < ApplicationController
def index
@tweets = Tweet.all
end
end
然后对应修改
app/views/tweets/index.html.erb
视图模版:
app/views/tweets/index.html.erb
视图<h1>Tweets</h1>
<ul>
<% @tweets.each do |tweet| %>
<li>
<%= tweet.title %>
</li>
<% end %>
</ul>
<p>Find me in app/views/tweets/index.html.erb</p>
这里视图模版中使用了 HTML 和 ERB
混合:
ERB
是将Ruby代码前乳文档的模版系统<% %>
表示 解析包含的Ruby代码<%= %>
表示 解析包含的Ruby代码并且将返回值输出
通过使用ERB,剋将常规Ruby程序嵌入ERB标记中,实现很多功能。需要注意的是,应该尽量保持ERB的tag内容短小,以方便代码阅读。
备注
数据库演示数据也可以通过 SQLite命令行交互 手工插入,这里我尝试了 SQLite current_timestamp 函数 插入案例增加了记录,这样就可以继续列表
上述简单的案例,就可以输出一个动态的列表展示在WEB页面