?
第19章 Active Record
这章介绍:
- >establish_connection方法
- >表、类、列、属性
- >id和关联关系
- >增删改查的方法
- >回调与事物处理
Active Record是Rails提供的对象关系映射(ORM)层,它帮助你实现了你的Model层。 这一章会涉及使用Active Record(后面简称AR)进行增删改查(CRUD)。最后会深入了解AR的生命周期(包括回调和事物)。
19.1 定义数据
在Depot项目中(可参考原书P70―P256)我们定义了一系列的models,其中有Order,它有一些属性例如String类型的email地址,除此之外,Rails提供了一个名为id的属性来标注该记录的主键(pk)。另外还有一些属性用于跟踪每一行的更新行为。Rails也提供了对象关系映射的功能,正如Depot中Order和LineItem的关系 组织表与列 每一个ActiveRecord::Base的子类,比如Order类,都包装了一个数据库表。默认情况下,AR假设表的名称关联到一个给定类名的复数形式,如果该类的名里包含大写字母,则该表名就会用下划线分割这些单词 如图: ?
大部分情况下Rails可以正确处理这些复数形式,但也有时你会不经意发现一些错误。比如可能你会遇到这种情况,你可以通过修改config/initializers/inflections.rb文件,让Rails理解英语的用法习惯情况
# Be sure to restart your server when you modify this file.
#
Add new inflection rules using the following format
#
(all these examples are active by default):
# ActiveSupport::Inflector.inflections do |inflect|
# inflect.plural /^(ox)$/i, '\1en'
# inflect.singular /^(ox)en/i, '\1'
# inflect.irregular 'person', 'people'
# inflect.uncountable %w( fish sheep )
# end
ActiveSupport::Inflector.inflections do |inflect|
inflect.irregular 'tax', 'taxes'
end
?
如果你要处理旧的表,并且不许要上述Rails自动关联行为可以自行指定
class Sheep < ActiveRecord::Base
self.table_name = "sheep"
# 设置表名
end
?
Ar的每一个实例都相当于一个数据库表中的行,而这些对象的属性相当于对应表中的列。在Depot项目重的Order实体中并没有提及任何关于orders表的列信息,因为AR是在运行时动态测定的。AR通过数据库的schema来配置包装表的那些实体类。
比如我们的orders表是通过下面的migration定义的(db/migrate/2011021100000007_create_orders.rb)
def self.up
create_table :orders do |t|
t.string :name
t.text :address
t.string :email
t.string :pay_type, :limit => 10
t.timestamps
end
end
?
我们可以通过rails console?命令来查看这个模型
首先获取所有的列
depot> rails console Loading development environment (Rails 3.0.5)
>> Order.column_names => ["id", "name", "address", "email", "pay_type", "created_at", "updated_at"]
然后我们查看pay_type列的具体信息
>> Order.columns_hash["pay_type"] => #<ActiveRecord::ConnectionAdapters::SQLiteColumn:0x7fe673f7da80 @name="pay_type", @null=true, @default=nil, @sql_type="varchar(10)", @type=:string, @scale=nil, @precision=nil, @primary=false, @limit=10>?
?
可见AR可以获取该列的所有信息。Rails在我们第一次使用Order类时通过底层数据库获取所有的信息 AR实例的属性通常和相关数据库表中的行信息相关连。例如,orders表可能包含以下信息
?
depot> sqlite3 -line db/development.sqlite3 "select * from orders limit 1" id = 1 name = Dave Thomas address = 123 Main St email = customer@example.com pay_type = Check created_at = 2010-06-18 00:36:57.355069 updated_at = 2010-06-18 00:36:57.355069
如果我们的AR对象获取到这个记录,该对象就会有七个属性如上
我们通过访问方法来访问这些属性。Rails自动转陪了属性读写方法
o = Order.find(1)
puts o.name
#=> "Dave Thomas
o.name = "Fred Smith"
?
给属性设值并不会改变数据库的数据,我们必须执行save才能持久化
Ar在读取属性值时会将其转换成有效的Ruby类型(如一个时间戳timestamp类型的数据会被当作Time类型的对象返回),如果我们希望得到原生数据,可以在该属性名称后加上_before_type_cast:
product.price_before_type_cast #=> "29.95", a string product.
updated_at_before_type_cast #=> "2008-05-13 10:13:14"
?
在model的代码重,我们可以使用 read_attribute和write_attribute私有方法,将属性名以String类型传入
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?SQL类型和Ruby类型的映射
?
Rails将没有小数位的Decimal转换成Fixnum对象,否则,转换成BigDecimal对象,为了确保不失精度。
至于Boolean的情况,由于不是所有的数据库都有boolean类型,为了防止像Mysql,0被当作false处理,1是true。不幸的是,Ruby把所有非false或nil都当作true处理,所以直接使用该值会出问题。通常我们在列名后加上问号
user = User.find_by_name("Dave")
if user.superuser?
grant_privileges
end
?
除了我们自己定义的属性,还有一些Rails自动提供的或有其他含义的属性。
关于Active Record提供的(其他)列
这里说明一些对于AR有特别意义的列的总结:
create_at, create_on, updated_at, updated_on
这几个列会在创建行或更新行的时候自动更新。以确保底层数据库列可以接受date,datetime, 或者string。Rails约定使用_on后缀的列标识date列,_at后缀标志time
lock_version
Rails会跟踪行版本号,如果表中有lock_version产生乐观锁
type
Ar可以被子类化,如果你这么做,所有的子类的属性都被保存在一个表中,而type属性就是用来标 注每一行的类型
id
这是表的主键的默认名称
xxx_id
这是默认的外键名,xxx是所引用的表名的单数形式
xxx_count
用于保持子表xxx的一个计数器缓存
还有一些插件,例如act_as_list,可能定义了其他的列名
主键和外键在数据库中是十分重要的,后面还会再探讨
19.2 定位与遍历记录
在Depot项目中,LineItem和其他三个模型有直接的关系:Cart,Order,Product.除此之外,模型之间有着间接联系。如Orders和Products通过LineItems维持这种关系
是id让这些关系成为可能
Ar的类和数据库中的表对应,类的实例与数据库表中每一行记录相对应。通过调用Order.find(1),可以返回包含这一主键为1的行的Order实例。
你可能随大流的往你所有的表结构中添加id主键列。然而如果你的数据库是旧的,AR给你了一个简单的方法来重写表主键名的方法。
比如这里一个books的数据库表的主键是ISBN
那么秩序在AR模型中如下指定
class LegacyBook < ActiveRecord::Base self.primary_key = "isbn" end
? 通常AR负责在我们向数据库中插入记录时为记录增加主键值。一般是增加的整形数,然而一旦我们重写了主键名,我们也许要承担设值主键的责任。我们依然需要设置一个名为id的属性。可能让我们感到惊讶的是,我们仍然需要设置id属性值来实现为主键赋值。在AR看来,主键值总是用id这个名称。 primary_key=xxx 设置了主键的名称。如下代码,我们通过给id属性设值,然而数据库中的主键的名称却是isbn:
book = LegacyBook.new book.id = "0-12345-6789" book.title = "My Great American Novel" book.save # ... book = LegacyBook.find("0-12345-6789") puts book.title # => "My Great American Novel" p book.attributes #=> {"isbn" =>"0-12345-6789", "title"=>"My Great American Novel"}
? 这里查看book的属性,只有isbn和title,却不见id。当你要设置主键,还要使用id。其他时间,使用实际的列名。 Model重定义了Ruby的id和hash方法来引用model的主键。这意味着有合法id的model对象可以当作hash的key来使用。也说明,没有保存的model对象不能作为hash的key(因为没有合法id) 如果两个model对象是同样的类型,并且主键值相等,则Rails认为他们相同(==)。即没有被持久化的model对象即使有不同的属性数据,也被看作是相同。所以,如果你发先你在比较未被持久化的对象则需要覆盖==方法。
?