ROR笔记(二)

6:视图模板可以访问控制器中的任何实例变量

7:添加数据库缺失字段 先创建一个迁移任务,取一个一目了然的名字,一般用create来创建表,add来给表增加字段。

ruby script/generate migration add_price # add_price为迁移任务名字

打开这个迁移任务源文件,在up方法内插入字段(向表里添加字段)

add_column:products,:price,:float #向products表插入float类型的字段price

在down方法内插入字段(向表里删除字段) remove_column:products,:price

运行迁移 rake db:migrate

8: 在模型层添加数据验证

validates_presence_of:title #检查 字段为title的值不为空

validates_numericality_of:price #检查字段为price的值为数值

validates_format_of:image_url,:with=>%r{\.(gif|jpg|png)$}i,:message=>”must be a url for a gif ,jpg, or png”

#检查image_url这个字段与with后的正则表达式是否匹配

SQL语句:

Active Record是如何处理SQL的,我们来看看find方法的:conditions参数,调用的时候像这样:find(:all,:conditions=>…),这里的:conditions参数决定了find方法将返回哪些记录,它相当于Sql语句的where部分,例如,要获取所有的名字为Dave,pay_type为po的订单,我们这样写:
pos = Order.find(:all,:conditions => “name = ‘dave’ and pay_type = ‘po'”)

find方法将返回所有符合条件的记录,并且都已经被转换成Order类的对象,如果没有符合条件的订单,find方法将返回一个长度为零的数组。

如果你的条件是预定的,那么上面的写法就很好了,但是如果我们要指定条件中的值呢,我们这样写:

name = params[:name]
pos = Order.find(:all,:conditions => “name = ‘#{name}’ and pay_type = ‘po'”)

但是,这并不是一个好主意,因为如果你的程序要发布在网络上的话,这样给SQL注入攻击留下了方便,更好的办法是,动态的生成SQL,让Active Record来处理它,我们可以在SQL语句中加入占位符,然后这些占位符将会在运行期被替换成指定的值,指定占位符的方法就是在SQL中使用问号,在运行时,第一个问号将被替换为集合的第二个元素的值,以此类推,例如,我们把上面的查询重写一次:

name = params[:name]
pos = Order.find(:all,:conditions => [“name = ? and pay_type = ‘po'”, name])

我们也可以使用带名字的占位符,名字前带冒号,例如下面这样:

name = params[:name]
pay_type = params[:pay_type]
pos = Order.find(:all,
:conditions => ["name = :name and pay_type = :pay_type",
{:pay_type => pay_type, :name => name}])

我也可以更进一步,因为params是一个hash,我们可以让conditions部分更简单

pos = Order.find(:all,
:conditions => ["name = :name and pay_type = :pay_type", params])

不管使用哪种占位符,Active Record都会很小心地处理SQL中的引号和escape。使用动态SQL,Active Record会保护我们不受SQL注入攻击。

Find方法详解:

首先,理解find(:first,…)方法是非常重要的,该方法在同样的conditions设置下,和find(:all,…)方法生成同样的sql语句,不同之处仅在于只返回一条记录。在调用的时候,一种的参数为:first,另一种为:all。

find方法执行一个select from这样的sql语句,:all标记指定返回表中所有的记录,:first返回第一条记录。但是现在:first还不能保证你得到的就是表中的第一条记录,原因是什么?:conditions参数指定了SQL语句的where部分,可以包含Sql语句或者包含可以替换的参数的名字,值。find方法并不能保证按照特定的顺序返回记录,除非指定查询的排序(order by)部分。:order参数就是用来指定SQL的排序条件的,下面的例子演示了查询名字为Dave的订单,并且按照pay_type,shipped_at字段进行降序排列。

orders = Order.find(:all,
:conditions => "name = 'Dave'",
:order => "pay_type, shipped_at DESC")

我们还可以设置:limit参数来限制返回的记录数,如果你使用:limit参数,或许还想指定排序条件,下面的例子返回10条记录,并且按照指定条件排序:

orders = Order.find(:all,
:conditions => "name = 'Dave'",
:order => "pay_type, shipped_at DESC",
:limit => 10)

参数:offset经常与:limit参数一同出现,用来指定从第一条记录起,返回指定的偏移量,下面代码演示了:offset参数的使用:

def Order.find_on_page(page_num, page_size)
find(:all,
:order => "id",
:limit => page_size,
:offset => page_num*page_size)
end

从上面的代码可以看到,这样使用find的场景就是分页显示数据,用pagesize指定每页的记录数,然后由pagenum*page_size指定从第几条开始提取数据。

find方法为我们构建了完整的Sql查询,而方法find_by_sql方法则允许我们对Sql有完整的控制,该方法只有一个参数,就是你想要使用的完整的sql语句,下面是示例代码:

orders = LineItem.find_by_sql("select line_items.* from line_items, orders " +
" where order_id = orders.id " +
" and orders.name = 'Dave Thomas' ")

现在有一个问题了,就是返回的Model对象中都包含有哪些属性呢?我们使用attributes( ), attribute_names( ), and attribute_present?( )方法来确定在Model对象中都包含有哪些属性,第一个返回一个hash,里面是键值对,第二个返回属性名的数组,第三个判断Model对象中是否含有某个属性,例如

orders = Order.find_by_sql("select name, pay_type from orders")
first = orders[0]
p first.attributes
p first.attribute_names
p first.attribute_present?("address")

程序的结果:

{"name"=>"Dave Thomas", "pay_type"=>"check"}
["name", "pay_type"]
false

find_by_sql方法也可以用来创建包含有派生(derived)(注1)的Model对象,如果你使用了as XXX 这样的sql来给派生字段一个别名,这个别名会作为Model中的属性名,例如:

items = LineItem.find_by_sql("select *, " +
" quantity*unit_price as total_price, " +
" products.title as title " +
" from line_items, products " +
" where line_items.product_id = products.id ")
li = items[0]
puts "#{li.title}: #{li.quantity}x#{li.unit_price} => #{li.total_price}"

在find_by_sql方法中,我们一样可以使用占位符来给Sql语句传递参数,例如:

Order.find_by_sql([“select * from orders where amount > ?”,
params[:amount]])

3 weeks ago, this page was being read.

,

Subscribe to Comments