本文简介 Active Record。
读完本文后,您将学到:
-
对象关系映射(Object Relational Mapping,ORM)和 Active Record 是什么,以及如何在 Rails 中使用;
-
Active Record 在 MVC 中的作用;
-
如何使用 Active Record 模型处理保存在关系型数据库中的数据;
-
Active Record 模式(schema)的命名约定;
-
数据库迁移,数据验证和回调。
Active Record 是 MVC 中的 M(模型),负责处理数据和业务逻辑。Active Record 负责创建和使用需要持久存入数据库中的数据。Active Record 实现了 Active Record 模式,是一种对象关系映射系统。
Active Record 模式出自 Martin Fowler 写的《企业应用架构模式》一书。在 Active Record 模式中,对象中既有持久存储的数据,也有针对数据的操作。Active Record 模式把数据存取逻辑作为对象的一部分,处理对象的用户知道如何把数据写入数据库,还知道如何从数据库中读出数据。
对象关系映射(ORM)是一种技术手段,把应用中的对象和关系型数据库中的数据表连接起来。使用 ORM,应用中对象的属性和对象之间的关系可以通过一种简单的方法从数据库中获取,无需直接编写 SQL 语句,也不过度依赖特定的数据库种类。
使用其他编程语言或框架开发应用时,可能必须要编写很多配置代码。大多数 ORM 框架都是这样。但是,如果遵循 Rails 的约定,创建 Active Record 模型时不用做多少配置(有时甚至完全不用配置)。Rails 的理念是,如果大多数情况下都要使用相同的方式配置应用,那么就应该把这定为默认的方式。所以,只有约定无法满足要求时,才要额外配置。
默认情况下,Active Record 使用一些命名约定,查找模型和数据库表之间的映射关系。Rails 把模型的类名转换成复数,然后查找对应的数据表。例如,模型类名为 Book
,数据表就是 books
。Rails 提供的单复数转换功能很强大,常见和不常见的转换方式都能处理。如果类名由多个单词组成,应该按照 Ruby 的约定,使用驼峰式命名法,这时对应的数据库表将使用下划线分隔各单词。因此:
-
数据库表名:复数,下划线分隔单词(例如
book_clubs
) -
模型类名:单数,每个单词的首字母大写(例如
BookClub
)
模型/类 | 表/模式 |
---|---|
|
|
|
|
|
|
|
|
|
|
根据字段的作用不同,Active Record 对数据库表中的字段命名也做了相应的约定:
-
外键:使用
singularized_table_name_id
形式命名,例如item_id
,order_id
。创建模型关联后,Active Record 会查找这个字段; -
主键:默认情况下,Active Record 使用整数字段
id
作为表的主键。使用 Active Record 迁移创建数据库表时,会自动创建这个字段;
还有一些可选的字段,能为 Active Record 实例添加更多的功能:
Note
|
虽然这些字段是可选的,但在 Active Record 中是被保留的。如果想使用相应的功能,就不要把这些保留字段用作其他用途。例如, |
创建 Active Record 模型的过程很简单,只要继承 ApplicationRecord
类就行了:
class Product < ApplicationRecord
end
上面的代码会创建 Product
模型,对应于数据库中的 products
表。同时,products
表中的字段也映射到 Product
模型实例的属性上。假如 products
表由下面的 SQL 语句创建:
CREATE TABLE products (
id int(11) NOT NULL auto_increment,
name varchar(255),
PRIMARY KEY (id)
);
按照这样的数据表结构,可以编写下面的代码:
p = Product.new
p.name = "Some Book"
puts p.name # "Some Book"
如果想使用其他的命名约定,或者在 Rails 应用中使用即有的数据库可以吗?没问题,默认的约定能轻易覆盖。
ApplicationRecord
继承自 ActiveRecord::Base
,后者定义了一系列有用的方法。使用 ActiveRecord::Base.table_name=
方法可以指定要使用的表名:
class Product < ApplicationRecord
self.table_name = "my_products"
end
如果这么做,还要调用 set_fixture_class
方法,手动指定固件(my_products.yml)的类名:
class ProductTest < ActiveSupport::TestCase
set_fixture_class my_products: Product
fixtures :my_products
...
end
还可以使用 ActiveRecord::Base.primary_key=
方法指定表的主键:
class Product < ApplicationRecord
self.primary_key = "product_id"
end
CURD 是四种数据操作的简称:C 表示创建,R 表示读取,U 表示更新,D 表示删除。Active Record 自动创建了处理数据表中数据的方法。
Active Record 对象可以使用散列创建,在块中创建,或者创建后手动设置属性。new
方法创建一个新对象,create
方法创建新对象,并将其存入数据库。
例如,User
模型中有两个属性,name
和 occupation
。调用 create
方法会创建一个新记录,并将其存入数据库:
user = User.create(name: "David", occupation: "Code Artist")
new
方法实例化一个新对象,但不保存:
user = User.new
user.name = "David"
user.occupation = "Code Artist"
调用 user.save
可以把记录存入数据库。
最后,如果在 create
和 new
方法中使用块,会把新创建的对象拉入块中,初始化对象:
user = User.new do |u|
u.name = "David"
u.occupation = "Code Artist"
end
Active Record 为读取数据库中的数据提供了丰富的 API。下面举例说明。
# 返回所有用户组成的集合
users = User.all
# 返回第一个用户
user = User.first
# 返回第一个名为 David 的用户
david = User.find_by(name: 'David')
# 查找所有名为 David,职业为 Code Artists 的用户,而且按照 created_at 反向排列
users = User.where(name: 'David', occupation: 'Code Artist').order(created_at: :desc)
active_record_querying.adoc会详细介绍查询 Active Record 模型的方法。
检索到 Active Record 对象后,可以修改其属性,然后再将其存入数据库。
user = User.find_by(name: 'David')
user.name = 'Dave'
user.save
还有种使用散列的简写方式,指定属性名和属性值,例如:
user = User.find_by(name: 'David')
user.update(name: 'Dave')
一次更新多个属性时使用这种方法最方便。如果想批量更新多个记录,可以使用类方法 update_all
:
User.update_all "max_login_attempts = 3, must_change_password = 'true'"
在存入数据库之前,Active Record 还可以验证模型。模型验证有很多方法,可以检查属性值是否不为空,是否是唯一的、没有在数据库中出现过,等等。
把数据存入数据库之前进行验证是十分重要的步骤,所以调用 save
和 update
方法时会做数据验证。验证失败时返回 false
,此时不会对数据库做任何操作。这两个方法都有对应的爆炸方法(save!
和 update!
)。爆炸方法要严格一些,如果验证失败,抛出 ActiveRecord::RecordInvalid
异常。下面举个简单的例子:
class User < ApplicationRecord
validates :name, presence: true
end
user = User.new
user.save # => false
user.save! # => ActiveRecord::RecordInvalid: Validation failed: Name can't be blank
active_record_validations.adoc会详细介绍数据验证。
Active Record 回调用于在模型生命周期的特定事件上绑定代码,相应的事件发生时,执行绑定的代码。例如创建新纪录时、更新记录时、删除记录时,等等。active_record_callbacks.adoc会详细介绍回调。
Rails 提供了一个 DSL(Domain-Specific Language)用来处理数据库模式,叫做“迁移”。迁移的代码存储在特定的文件中,通过 rails
命令执行,可以用在 Active Record 支持的所有数据库上。下面这个迁移新建一个表:
class CreatePublications < ActiveRecord::Migration[5.0]
def change
create_table :publications do |t|
t.string :title
t.text :description
t.references :publication_type
t.integer :publisher_id
t.string :publisher_type
t.boolean :single_issue
t.timestamps
end
add_index :publications, :publication_type_id
end
end
Rails 会跟踪哪些迁移已经应用到数据库上,还提供了回滚功能。为了创建表,要执行 rails db:migrate
命令。如果想回滚,则执行 rails db:rollback
命令。
注意,上面的代码与具体的数据库种类无关,可用于 MySQL、PostgreSQL、Oracle 等数据库。关于迁移的详细介绍,参阅active_record_migrations.adoc。