当前位置: 代码迷 >> Web前端 >> 通译《应用Rails进行敏捷Web开发(第四版)》(二)
  详细解决方案

通译《应用Rails进行敏捷Web开发(第四版)》(二)

热度:251   发布时间:2012-10-20 14:12:48.0
翻译《应用Rails进行敏捷Web开发(第四版)》(二)

?

第19章 Active Record

这章介绍:
  • >establish_connection方法
  • >表、类、列、属性
  • >id和关联关系
  • >增删改查的方法
  • >回调与事物处理

Active RecordRails提供的对象关系映射(ORM)层,它帮助你实现了你的Model层。 这一章会涉及使用Active Record(后面简称AR)进行增删改查(CRUD)。最后会深入了解AR的生命周期(包括回调和事物)。


19.1 定义数据

Depot项目中(可参考原书P70―P256)我们定义了一系列的models,其中有Order,它有一些属性例如String类型的email地址,除此之外,Rails提供了一个名为id的属性来标注该记录的主键(pk)。另外还有一些属性用于跟踪每一行的更新行为。Rails也提供了对象关系映射的功能,正如DepotOrderLineItem的关系 组织表与列 每一个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_attributewrite_attribute私有方法,将属性名以String类型传入



? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?SQL类型和Ruby类型的映射

?

Rails将没有小数位的Decimal转换成Fixnum对象,否则,转换成BigDecimal对象,为了确保不失精度。 至于Boolean的情况,由于不是所有的数据库都有boolean类型,为了防止像Mysql,0被当作false处理,1true。不幸的是,Ruby把所有非falsenil都当作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 这几个列会在创建行或更新行的时候自动更新。以确保底层数据库列可以接受datedatetime 或者stringRails约定使用_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.除此之外,模型之间有着间接联系。如OrdersProducts通过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的属性,只有isbntitle,却不见id。当你要设置主键,还要使用id。其他时间,使用实际的列名。 Model重定义了Rubyidhash方法来引用model的主键。这意味着有合法idmodel对象可以当作hashkey来使用。也说明,没有保存的model对象不能作为hashkey(因为没有合法id) 如果两个model对象是同样的类型,并且主键值相等,则Rails认为他们相同(==)。即没有被持久化的model对象即使有不同的属性数据,也被看作是相同。所以,如果你发先你在比较未被持久化的对象则需要覆盖==方法。

?

  相关解决方案