您的位置:首页 > 产品设计 > 产品经理

Agile Web Development with Rails 翻译(十)

2006-09-11 14:53 387 查看
Agile Web Development with Rails 翻译(十)

这就是为什么New products表单已经带有知道的title,description,image和price字段—因为它们在数据库表中,它们已经被添加到了“模型”中。通过“支架”表单“生成器”可以向“模型”要求有关这些字段的信息,然后就使用它找到的字段来创建个合适的HTML表单。

2006年4月16日更新

6.3 循环 A3: 确认!

经过两次的反复,我们的客户注意到,如果它输入一个无效的单价,或者忘记设置产品的描述信息,应用程序会接受这些并添加到数据库中。丢失产品的描述信息还只是个麻烦,但是单价若为零元则要损失金钱,所以客户要求我们添加对应用程序的确认工作。如果产品的文本字段为空,或一个无效图片的URL,或无效的单价,它们则不应该被存到数据库中。
但是,我们把确认放在哪里呢?
“模型”层是代码与数据库之间的守护者。我们的应用程序访问数据库时没做任何事,或者将数据存回到数据库时也没有通过“模型”。那么就把所有的确认工作放在这里;不管数据的流向如何都不会有问题。如果在写到数据库之前,“模型”检查它,就可以阻止损坏的数据到数据库中。
让我们看看“模型”类的源代码(在app/models/product.rb内)。
class Product < ActiveRecord::Base
end
这什么也没有?当然,所有重体力活(数据库映射,创建,更新,搜索,等等)都被父类(ActiveRecor::Base,Rails的一部分)完成了。由于继承的关系,我们的Product类自然地继承了所有功能。
添加我们的确认应该很容易。让我们通过确认所有写到数据库内文本字段开始。我们只是添加一些代码到现有的“模型”内。
class Product < ActiveRecord::Base
validates_presence_of :title, :description, :image_url

end

validates_presence_of()方法是个标准的Rails确认器。它检查给定的字段,或设置字段,以及它们内容是否为空。图6.6显示了如果你试着提交一个没有填充完字段的产品时,会发生什么事。给人印象是:有错误的字段被高亮度显示,然后错误被总结出来放到顶部的表单内。没有用于错误的代码。你也可能注意到,在编辑完product.rb文件后你并没有重启动应用程序来测试你的修改—在开发模式中,Rails会注意到被修改的文件并重新加载它到应用程序中。这是促进开发的重大生产力。



现在我们想确认单价是否是有效的正整数。我们分再步解决这个问题。首先,我们使用validates_numericality_of()方法来确认单价是否是有效的数字。
validates_numericality_of :price

现在如果我们用无效的单价添加产品时,就会出现相应的消息。[MySQL给Rails足够的元数据使它知道单价包含了数字,所以Rails转换它到个浮点值。对于其它数据库,这个值可能是字符串支持的,所以在用于比较之前,你需要使用Float(price)来转换它。]



接着我们要检查它是否大于零。在我们的“模型”类中,我们可写个方法validate()来做这件事。Rails在保存我们产品的实例之前会自动地调用这个方法。所以我们可以用它检查字段的效性。我们将这个方法设置为protected,因为它不应该从“模型”的上下文环境的外部被调用。
protected
def validate
errors.add(:price, "should be positive") unless price.nil? || price > 0.0
end
如果单价小于或等于零,validation方法使用errors.add(…)来记录这个错误。这样做会阻止Rails写入错误数据到数据库中。它也显示了友好的信息给用户。传递给errors.add()的第一个参数是字段的名字,第二个参数是消息的内容。注意,我们只检查了单价的设置。如果没有这些额外的设置我们就不能比较nil与0.0,并且会引发一个异常。
用于确认的另外两件事是:首先我们想确保每个产品有个唯一的标题。在“模型”的Product内有行代码可完成这件事。唯一性确认使用个简单的检查来确保,products表内没有与我们要保存的这个产品同名的行。
validates_uniqueness_of :title

最后,我们需要确认输入的图像URL的有效性。我们使用validates_format_of()方法来做,它用正则表达式来匹配一个字段。现在我们只检查URL的http:开始部分,以及是否以.gif,.jpg或.png结尾。[稍后,我们或许想修改这个形式以让用户从一个有效的images列表中选择一个,但是们还想保持对一些有恶意的人提供数据的确认。]
validates_format_of :image_url,
:with => %r{^http:.+.(gif|jpg|png)$}i,
:message => "must be a URL for a GIF, JPG, or PNG image"
到现在,我们已经添加了这些用于检查的确认
1、字段title,description,和image URL是否为空。
2、price是否是大于零的有效数字。
3、title是否是唯一名字。
4、image URL看起来是否合理。
这是“模型”内的Product类更新后的完整列表。
class Product < ActiveRecord::Base
validates_presence_of :title, :description, :image_url
validates_numericality_of :price
validates_uniqueness_of :title
validates_format_of :image_url,
:with => %r{^http:.+.(gif|jpg|png)$}i,
:message => "must be a URL for a GIF, JPG, or PNG image"
protected
def validate
errors.add(:price, "should be positive") unless price.nil? || price > 0.0
end
end
在接近尾声时,我们要求我们客户在用次应用程序,它很高兴。它只看了几分钟,但简单的确认动作会让产品管理页面感觉很牢固了。

“控制器”处理来自浏览器的“请求”。一个应用程序可以有多个“控制器”。对于我们的Depot应用程序来说,我们最终会有二个“控制器”,一个用于处理卖方的站点管理,另一用于处理买方。我们在Admin“控制器”内创建产品维护的“支架”,这就是为什么在URL内admin是它的第一个路径部分。
这个工具生成的Rails “支架”使用Ruby代码来组装你的应用程序目录树。如果你检查它,你会现有个完整的应用程序构架—其内已经放置了Ruby代码;这些都是源代码,而不是简单地对一些标准库的调用。对我们来说,这是个好消息,因为它意味着我们可以修改“支架”内产生的代码。“支架”是一个应用程序的起点,而不应用程序的终点。

-------------------------------------------------------------------
David 说. . .
我们能更换所有的“支架”吗?

大多数是这样的。“支架”不是应用程序开发的目的。它只是在我们构建应用程序时提供支持。当你设计出产品的列表该如何工作时,你依赖于“支架”“生成器”产生创建,更新,和删除的行为。然后在保留这个“动作”时你要替换由“生成器”生成的行为。有时候当你需要一个快速接口时,并且你并不在乎界面的丑陋,“支架”就足够用了。不要指望“支架”能满足你程序的所有需要。
-------------------------------------------------------------------

6.2 循环 A2: 添加被遗漏的列

我们展示基于“支架”的代码给们的客户,并解释这还很粗糙。它们会很高兴这么快就看到一些东西可以工作了。当客户试用一会之后,它会注意到我们最初的讨论中有些东西被遗漏了。查看显示在浏览器窗口内的产品信息,很明显需要我们添加个日期列—过期的产品不应该给消费者。
这就意味着们要添加一列到数据库表中,并且我们还要确保不同的管理页面都要被更新并支持这个新列。
一些开发者(和DBA)可能会这样做:
alter table products
add column date_available datetime;
相反,我倾向于维护一个我最初用来创建的,包含DDL的文本文件。这样我会有历史记录,并在一个单个文件包含我需要重新创建的所有命令。所以让们选择文件db/create.sql,并添加date_available列。
drop table if exists products;

create table products (
id int not null auto_increment,
title varchar(100) not null,
description text not null,
image_url varchar(200) not null,
price decimal(10,2) not null,
date_available datetime not null,

primary key (id)
);
当我第一次创建这个文件时,我添加一个drop表命令在它的顶端。现在这允许我们在命令行创建一个新(空的)计划实例。
depot> mysql depot_development <db/create.sql
很明显,这种方式只对现有数据库内的表没重要数据才可行。在开发期间这没问题,但在发行产品中我们需要更小心些。一旦一个应用程序被发行,我们会移走我们数据库计划脚本。
即使在这期间,这也是个痛苦,因为我们需要重新输入我们测试数据。通常我在开发期间若输入了些数据的话,我会从数据库中转储它们,然后在我每次执行计划时再重新加载它们。
计划已经更改了,所以我们“支架”代码就变得过时了。因为我还没有修改这些代码,所以可以安全地重新产生它。注意当“生成器”脚本覆写一个文件时,它会提示我们。我们输入指令a来允许它覆写所有文件。
depot> ruby script/generate scaffold Product Admin

dependency model
exists app/models/
exists test/unit/
exists test/fixtures/
skip app/models/product.rb
skip test/unit/product_test.rb
skip test/fixtures/products.yml
exists app/controllers/
exists app/helpers/
exists app/views/admin
exists test/functional/
overwrite app/controllers/admin_controller.rb? [Ynaq] a

forcing scaffold
force app/controllers/admin_controller.rb
force test/functional/admin_controller_test.rb
force app/helpers/admin_helper.rb
force app/views/layouts/admin.rhtml
force public/stylesheets/scaffold.css
force app/views/admin/list.rhtml
force app/views/admin/show.rhtml
force app/views/admin/new.rhtml
force app/views/admin/edit.rhtml
create app/views/admin/_form.rhtml

刷新浏览器,并创建个新产品,你会看到像图6.5样的东西。(如果不一样的话,可能“生成器”还在等着你输入a)现在我们有了date字段,这是反馈信息的功劳。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: