第四部分 结合 Ruby 和 Rails
第十四章 再次为 R4RMusic 应用领域建模
一、跟踪 ActiveRecord 模型实例的功能
1. 模型实例功能概览:Rails 模型实例的功能来源于四个地方
■ 通过实例所属的类继承,该实例可以调用所属类的父类(即 ActiveRecord::Base 或该类的另外一个后代)的实例方法。
■ 根据相关的数据库表的字段名自动生成的读写方法(accessor)和其它方法。举例来说,由于数据库表 composers 中有 title 字段,所以 Composer 的对象具有 title 和 title= 方法。
■ 在使用关联指令时,半自动生成的读写方法和其它方法。例如,在 Work 类中的 has_one: composer。
■ 通过编程添加的任意多个实例方法,它们根据需要被添加到模型定义文件中。
2. 继承的和自动获得的 ActiveRecord 模型实例行为
(1)ActiveRecord 对象的两种存在方式
ActiveRecord 和典型的 Ruby 对象之间是有差别的,ActiveRecord 对象有两种存在方式:一方面,它是一个 Ruby 类。另一方面,它是一个句柄,可以用它来直接操作数据库记录。
ActiveRecord 的类方法和实例方法,以及它们与对象和数据库记录的存在状态之间的关系
| new | create (new+save) | find | save | update | delete (find+destroy) | destroy |
方法调用前: | |||||||
Ruby 对象存在吗? | 不存在 | 不存在 | 不存在 | 存在 | 存在 | 不存在 | 存在 |
数据库记录存在吗? | 不存在 | 不存在 | 存在 | 存在 | 存在 | 存在 | 存在 |
方法调用后: | |||||||
Ruby 对象存在吗? | 存在 | 存在 | 存在 | 存在 | 存在 | 存在 冻结 | 存在 冻结 |
数据库记录存在吗? | 不存在 | 存在 | 存在 | 存在 | 存在 | 不存在 | 不存在 |
■ 方法被调用前没有对象存在的方法是类方法(可以根据没有对象实例调用它们来判断)。
■ 冻结意味着已经通过 Ruby 的 freeze 方法将对象实例冻结。这保证对象不会再被改变,不能再给它的实例变量赋值。在销毁一个 ActiveRecord 对象后,它会被冻结。尽管可以从数据库中彻底删除记录,但是 Ruby 中没有相应的可以彻底地删除对象的操作。因此,冻结它就是最好的指示它的生命期已经结束的方法。
■ update、delete 和 destroy 各自有一个以 _all 结尾的变体(update_all 等),它们对数据库中所有已存的记录或相应的 Ruby 实例执行给定的操作。
(2)根据数据库中的字段名自动生成的方法
ActiveRecord 将数据库的结构和名字都映射到 Ruby 中,将表名映射为类名,字段名映射为实例方法。Rails 是一个对象关系映射系统(ORM,Object/Relational Mapping)。
3. 通过关联半自动获得的行为
关联准确生动地描述了 Rails 实体模型之间的“有”和“属于”关系。如 Composer 类中包含 has_many :works,以及 Work 类中包含 belongs_to :composer。
我们没有定义 has_many 方法,所以它肯定是父类 ActiveRecord::Base 的类方法,其它的关联和有 belongs_to、has_one 和 has_and_belongs_to_many。
二、改进领域模型
1. 从 Edition 模型中抽取出 Publisher 模型,并设置发行商和版本之间的关系
F:\ruby_project\R4Rmusic>ruby script/generate model publisher
修改 app/models/publisher.rb 文件
class Publisher < ActiveRecord::Base
end
创建 publishers 数据表结构
CREATE TABLE publishers (
);
改变后的 editions 表以及新生成的 publishers 表
2. instruments 模型和多对多关系
(1)我们要支持按照乐器分类来浏览乐谱,因此单独创建一个乐器的数据表
F:\ruby_project\R4Rmusic>ruby script/generate model instrument
instruments 模型有两个属性:name(乐器名,如小提琴) 和 family(乐器族,如弦乐)
CREATE TABLE instruments (
);
乐器和音乐之间是多对多关系:一件作品可以被多个乐器演奏,一个乐器可以演奏多个作品。
实现多对多关系,首先要为 Rails 提供一个记录乐器和作品关系的表,并遵循 Rails 标准为这张表命名:instruments_works。它有两个字段:一个是针对特定乐器ID字段,另一个是针对特定作品的ID字段
CREATE TABLE instruments_works (
);
(2)使用 has_and_belongs_to_many 关联
修改 app/models/instrument.rb 中的关联指令
class Instrument < ActiveRecord::Base
end
添加 app/models/work.rb 中的关联指令
class Work < ActiveRecord::Base
end
(3)instrument 模型对 work 模型的影响
需要给 Work 模型添加基调(key)属性,但是 key 是 SQL 的关键字,所以改名为 kee。
还需要增加一个作品编号(opus)字段,现在 works 表的 SQL 如下
CREATE TABLE works (
);
(4)editions 到 works 的多对多映射
一件作品可以出现在多个版本中,一个版本可以包含多件作品。首先把 work_id 从 editions 表中删除,然后增加一个用于保存 works 和 editions 关系的表
CREATE TABLE editions_works (
);
修改 ActiveRecord,修改 edition.rb 中的 belongs_to :work
has_and_belongs_to_many :works
在 work.rb 中加入下面代码(:order 告诉 ActiveRecord 把返回的版本按年份升序排序)
has_and_belongs_to_many :editions,
3. 创建顾客和订单模型
(1)我们将让顾客做下面这些事:
■ 注册
■ 登录
■ 选择商品并将其放入购物车中
■ 结帐(购买商品)
在 Rails 方面,需要为 customer 模型生成模型和控制器
F:\ruby_project\R4Rmusic>ruby script/generate model customer
F:\ruby_project\R4Rmusic>ruby script/generate controller customer
在数据库方面,创建顾客模型的 SQL 表结构
CREATE TABLE customers (
);
(2)创建定单模型
F:\ruby_project\R4Rmusic>ruby script/generate model order
为记录订单时间,orders 表有一个名为 created_at 字段。该字段名在 Rails 中是一个特殊的字段名:在保存 Order 对象时,Rails 自动将保存操作发生的时间和日期。Order 模型中还包含一个 status 字段,如果订单已经付过款,该字段设置为“paid”,如果订单商品已经发出,则设置为“shipped”
CREATE TABLE orders (
);
(3)建立 orders 与 customers 和 editions 的关联
修改 app/models/orders.rb 文件
class Order < ActiveRecord::Base
end
修改 app/models/customer.rb 文件,并创建依赖关系:如果顾客记录不存在,那么他的订单也随之删除,一旦所属的记录被删除,这些无主记录就没有用了。
class Customer < ActiveRecord::Base
end
最后在 edition 模型中,也要为 orders 添加一个关联
class Edition < ActiveRecord::Base
end
4. 为订单设置默认状态
在 Order 类中定义 before_create 方法,在每次保存新模型实例时,自动调用该方法
def before_create
end
5. 图示新的领域模型