27、Active Record 基础(一)

来源:互联网 发布:官方三菱plc编程软件 编辑:程序博客网 时间:2024/05/21 10:03

        Active RecordRails中提供的对象关系映射(ORM)层。在这一章,我们将看到Active Record的基础知识:连接数据库,映射表,操作数据等。 下一章我们将深入这些东西的细节中。

    Active Record非常接近标准的ORM模型:表对应类,行对应对象,列对应对象的属性。它在配置方面与大多数其他ORM框架不同。通过一组默认的规则,Aactive Record使得开发人员在配置上的工作非常小。要说明这一点,这儿有个程序,它使用Active Record包装MySQL数据库中的一个表orders。在用一个特定的id找到定单之前,它更改购买者的姓名,并将结果存回到数据库中,改变了原始记录行。

require "rubygems"

require_gem "activerecord"

ActiveRecord::Base.establish_connection(:adapter => "mysql",

:host => "localhost", :database => "railsdb")

class Order < ActiveRecord::Base

end

order = Order.find(123)

order.name = "Dave Thomas"

order.save

这就是全部的代码。在这个例子中没有要求任何配置信息(除了数据库连接)。Active Record是怎样知道我们所需要的,又是怎样知道正确地得到它呢?让我们看看这里面的原理。

 

14.1 表与类

 

当你创建一个ActiveRecord::Base的子类时,实际上是包装一个数据库表。缺省情况下,Active Record假定表名字是类名字的复数形式。如果类名包含多个以大写字母开头的单词,表名会假定以下划线分隔这些单词。一些无规律的复数形式也会被处理。

 

 

这些规则反应了DHHrails的作者)的理念:类名字应该是单数,而表名字应该是复数。如果你不喜欢这种做法,你可以在配置文件中设置一个全局变量关闭它(config目录下的environment.rb文件)

ActiveRecord::Base.pluralize_table_names = false

用于让表名字成为复数的算法很简单。多数时候它会正常工作,便如何你的类名字是Sheep,它会试着查找名为sheeps的表。对表名字和类名字的这种假设关系也可能会出问题,如果你用个先前的schema操作的话,[The meaning of the word schema varies across the industry. We use it to mean the definition of tables and their interrelationships in the context of an application or suite of related applications. Basically, the schema is the database structure required by your code. ]否则表的名字可能强迫你在代码中写陌生的和不合需要的类名字。基于这个原因,Active Record允许你使用set_table_name指令地覆写缺省生成的表名字。

class Sheep < ActiveRecord::Base

set_table_name "sheep" # Not "sheeps"

end

class Order < ActiveRecord::Base

set_table_name "ord_rev99_x" # Wrap a legacy table...

end

 

-------------------------------------------------------

David . . .

我的属性在哪儿?

 

The notion of a database administrator (DBA) as a separate role from programmer has led some developers to see strict boundaries between code and schema. Active Record blurs that distinction, and no other place is that more apparent than in the lack of explicit attribute definitions in the model.

But fear not. Practice has shown that it makes little difference whether you’re looking at a database schema, a separate XML mapping file, or inline attributes in the model. The composite view is similar to the separations already happening in the Model-View-Control pattern—just on a smaller scale.

Once the discomfort of treating the table schema as part of the model definition has dissipated, you’ll start to realize the benefits of keeping DRY. When you need to add an attribute to the model, you simply change the schema, which automatically retains your data (use alter instead of drop/create), and reload the application.

Taking the “build” step out of schema evolution makes it just as agile as the rest of the code. It becomes much easier to start with a small schema and extend and change it as needed.

--------------------------------------------------------

 

14.2 列与属性

 

Active Record对象对应数据库表中的行。对象的属性对应表中的列。你可能已经注意到我们定义的Order类并没有提到orders表中任何列。这是因为Active Record是在运行期间动态决定它们。Active Record在数据库端的schema上反射,以配置包装表的类。[这不是严格的事实,就像一个model可以有不是schema的一部分的属性。我们在272页的下一章将祥细讨论。]

我们的表orders可能是如下SQL语句创建的。

create table orders (

id int not null auto_increment,

name varchar(100) not null,

email varchar(255) not null,

address text not null,

pay_type char(10) not null,

shipped_at datetime null,

primary key (id)

);

我们创建一个包装这个表的Active Record类。

require 'rubygems'

require_gem 'activerecord'

# Connection code omitted...

class Order < ActiveRecord::Base

end

一旦我们定义了Order类,我们就可以查询到它包含的属性()的信息。下面的代码使用了columns()方法,它返回一个Column对象数组。这里我们只显示orders表的每个列的名字,通过一个hash导出shipped_at这个列的细节。

require 'pp'

pp Order.columns.map { |col| col.name }

pp Order.columns_hash['shipped_at']

当我们运行这段代码时,我们得到以下输出。

["id", "name", "email", "address", "pay_type", "shipped_at"]

#<ActiveRecord::ConnectionAdapters::Column:0x10e4a50

@default=nil,

@limit=nil,

@name="shipped_at",

@type=:datetime>

注意:Active Record决定每列的类型。在这个例子中,它已经知道数据库中的shipped_at列是一个datetime类型。它将从这个列得到的值存放在Ruby内的Time对象中。我们可以通过写入一个时间字符串到这个属性,然后再取回此内容来验证。你会发现它们返回的是Ruby Time对象。

order = Order.new

order.shipped_at = "2005-03-04 12:34"

pp order.shipped_at.class

pp order.shipped_at

它会产生:

Time

Fri Mar 04 12:34:00 CST 2005

14.1显示了SQL类型和它们的ruby表达方式之间的的映射关系。通常这个映射关系是显而易见的。只有一个潜在的与decimal列有关的问题。schema设计者会使用decimal列来存储带有固定小数位的数字--decimal列要求十分精确。Active Recorddecimal列映射为Float类。尽管这对大多数程序是可行的,但是浮点数字是不精确的,如果在这个类型的属性上执行一系列操作,可能会发生四舍五入的问题。你也可能想使用多个integer列来替代,存储货币值的各单元,分,角,元等。另一种做法是使用聚合来构造Money对象以便分离数据库的列(dollars centspounds pence,等)。

 

 

访问属性

 

如果一个model对象有个属性名为balance,你可以使用索引操作符,传递给它一个字符串或一个符号(symbol)来访问这个属性的值。这儿是我们将使用的符号。

account[:balance] #=> return current value

account[:balance] = 0.0 #=> set value of balance

尽管,在普通编码中不赞成这样使用,但是它可能会减少你的选择,当将来你可能对属性基础实现进行修改时。相反,你应该访问值或使用Ruby存取器方法来访问model属性。

account.balance #=> return current value

account.balance = 0.0 #=> set value of balance

使用这两种技术返回的值,如果可能话将被Active Record强制转换一个适当的Ruby类型(所以,例如,如果数据列是个timestamp,则一个Time对象将会被返回)。如果你想获得一个属性的原始值,append_before_type_case方法返回它的名字,像下面代码显示的。

account.balance_before_type_cast #=> "123.4", a string

account.release_date_before_type_cast #=> "20050301"

最后,在model本身的代码内部,你可使用read_attribute()write_attribute()私有方法。这些接受做为字符串参数的属性的名字。

 

------------------------------------------------

David . . .

覆写Model属性

这儿是个说明使用存取器获得model属性的好处的例子。我们的account model将立即引发个异常,只要是有人试图将balance设置成低于最小值的一个值。

class Account < ActiveRecord::Base

def balance=(value)

raise BalanceTooLow if value < MINIMUM_LEVEL

self[:balance] = value

end

end

 

 

 

 

 

 

 

 

原创粉丝点击