Skip to content

Ruby on Rails - Active Record

Active Record (活动记录) 是 MVC (模型-视图-控制器) 中的 M——模型层。它是 Rails 提供的对象关系映射 (ORM) 系统。Active Record 允许您像操作 Ruby 对象一样与数据库表进行交互,抽象掉了大部分 SQL 语句。

Active Record 的核心原则:

  • 表映射到类:一个数据库表(例如,books)对应一个 Ruby 类(例如,Book)。
  • 行映射到对象:表中的每一行代表该类的一个实例(对象)。
  • 列映射到对象属性:表中的列对应于对象的属性。

Active Record 提供了用于 CRUD (创建、读取、更新、删除) 操作的方法,以及定义模型之间的关联、验证、回调和数据库查询等。

在为 Rails 应用程序设计数据库 schema (结构) 时,请遵循以下约定:

  • 表命名:表名使用复数和小写(例如,books, authors)。模型类名使用单数和驼峰命名法 (CamelCased)(例如,Book, Author)。
  • 主键:每个表应有一个 id 列作为其主键(默认为自增整数)。Active Record 会自动管理这一点。
  • 外键:对于关联(例如,一本书属于一个作者),拥有 belongs_to 关联的表(例如,books)将有一个外键列,其命名方式是关联模型的名称加上 _id 后缀(例如,author_id)。
  • 时间戳:习惯上在表中包含 created_at 和 updated_at 时间戳列。如果这些列存在,Active Record 会自动管理它们。

您可以使用 Rails 生成器命令生成模型文件。对于我们的图书库应用程序,让我们创建 Book 和 Subject 模型。

$ bin/rails generate model Book title:string price:decimal subject:references description:text
$ bin/rails generate model Subject name:string

这个命令做了几件事:

  • 创建模型文件:app/models/book.rb 和 app/models/subject.rb。
  • 在 db/migrate/ 中创建迁移文件,用于定义 books 和 subjects 表的 schema。Book 生成器中的 subject:references 会自动添加一个 subject_id 列,并在模型中建立 belongs_to 关联(如果可能)。
  • 创建测试文件(例如,test/models/book_test.rb)和 fixtures 文件。

生成的 app/models/book.rb 可能看起来像这样(如果 subject:references 有效使用了的话):

class Book < ApplicationRecord
belongs_to :subject # Added by 'subject:references'
end

以及 app/models/subject.rb:

class Subject < ApplicationRecord
end

注意:模型现在继承自 ApplicationRecord,而 ApplicationRecord 本身继承自 ActiveRecord::Base。这为应用程序范围内的模型配置提供了一个通用位置。

关联定义了模型之间的关系。常见的关联类型包括:

  • belongs_to:表示一对一或一对多连接,其中该模型拥有外键(例如,一个 Book 属于一个 Subject)。
  • has_many:表示一对多关系(例如,一个 Subject 有多本 Book)。
  • has_one:表示一对一关系(例如,一个 User 有一个 Profile)。
  • has_and_belongs_to_many (HABTM):用于使用连接表直接建立多对多关系。
  • has_many :through:一种更灵活的建立多对多关系的方式,通常比 HABTM 更受欢迎,因为它允许在连接模型上定义属性。

让我们确保我们的 Book 和 Subject 模型具有正确的关联。假设一本书属于一个主题,而一个主题可以有多本书:

app/models/book.rb
class Book < ApplicationRecord
belongs_to :subject
# ... validations etc. ...
end
// app/models/subject.rb
class Subject < ApplicationRecord
has_many :books
# ... validations etc. ...
end

Book 模型生成器中的 subject:references 应该已经添加了 belongs_to :subject。我们需要手动向 Subject 添加 has_many :books。

验证确保只有有效的数据才能保存到数据库中。它们在模型中定义。

常见的验证助手:

  • validates :attribute, presence: true (替代 validates_presence_of)
  • validates :attribute, uniqueness: true (替代 validates_uniqueness_of)
  • validates :attribute, numericality: true (替代 validates_numericality_of)
  • validates :attribute, length: { minimum: 5, maximum: 100 }
  • validates :attribute, format: { with: /regex/ }
  • validates :attribute, inclusion: { in: %w(small medium large) }
  • validates :attribute, exclusion: { in: %w(admin root) }

让我们向 app/models/book.rb 添加一些验证:

app/models/book.rb
class Book < ApplicationRecord
belongs_to :subject
validates :title, presence: true, uniqueness: { scope: :subject_id, message: "should be unique for each subject" }
validates :price, numericality: { greater_than_or_equal_to: 0, message: "must be a positive number or zero" }, allow_nil: true
validates :description, length: { maximum: 500 }
end
// app/models/subject.rb
class Subject < ApplicationRecord
has_many :books
validates :name, presence: true, uniqueness: true
end

当您尝试保存一个 Book 或 Subject 对象(例如,book.save)时,这些验证会自动被检查。如果验证失败,save 返回 false,并且可以通过 book.errors.full_messages 访问错误信息。

Active Record 提供了丰富的查询接口:

  • Book.all # 检索所有图书
  • Book.find(1) # 查找 ID 为 1 的图书
  • Book.find_by(title: 'Rails Guide') # 查找标题为 ‘Rails Guide’ 的第一本图书
  • Book.where(subject_id: 2) # 查找所有属于主题 ID 为 2 的图书
  • Book.where('price > ?', 50) # 价格大于 50 的图书
  • Book.order(created_at: :desc) # 按创建日期降序排列图书
  • Book.limit(10) # 限制结果为 10 条
  • Book.joins(:subject).where(subjects: { name: 'Programming' }) # 连接主题表并过滤

这些方法可以链式调用以构建复杂的查询。

定义模型之后,下一步是使用 Migrations (数据库迁移) 创建或更新数据库 schema。数据库迁移允许您以一致且版本控制的方式随着时间推移演变数据库结构。