当前位置:首页 > ActiveRecord(Ruby)
# sum the total value
order_list = params[:order_ids] orders = Order.find(order_list) count = orders.size
通常,在查询的时候都要用到除过id以外的值,Active Record提供了一组设置来执行这些查询,我们会介绍find使用方法,从基本的查询,再到高阶些的动态查询。 到现在我们只是了解了find方法的最基本的内容,通过指定id来获取一个或一组对象。另外,我们还可以使用一些标记比如:first,:all来作为find方法的参数。
:first将返回符合条件的第一条记录,:all将返回所有符合条件的记录,下一篇我们来看看Active Record是如何处理sql的。
想象一下Active Record是如何处理SQL的,我们来看看find方法的:conditions参数,调用的时候像这样:find(:all,:conditions=>?),这里的:conditions参数决定了find方法将返回哪些记录,它相当于Sql语句的where部分,例如,要获取所有的名字为Dave,pay_type为po的订单,我们这样写:
pos = Order.find(:all,:conditions => \
find方法将返回所有符合条件的记录,并且都已经被转换成Order类的对象,如果没有符合条件的订单,find方法将返回一个长度为零的数组。 如果你的条件是预定的,那么上面的写法就很好了,但是如果我们要指定条件中的值呢,我们这样写:
# get the limit amount from the form name = params[:name] # DON'T DO THIS!!!
pos = Order.find(:all,:conditions => \
但是,这并不是一个好主意,因为如果你的程序要发布在网络上的话,这样给SQL注入攻击留下了方便(后面我们在关于安全的主题里会讨论SQL注入的话题),更好的办法是,动态的生成SQL(注1),让Active Record来处理它,我们可以在SQL语句中加入占位符,然后这些占位符将会在运行期被替换成指定的值,指定占位符的方法就是在SQL中使用问号,在运行时,第一个问号将被替换为集合的第二个元素的值,以此类推,例如,我们把上面的查询重写一次:
name = params[:name]
pos = Order.find(:all,:conditions => [\ 我们也可以使用带名字的占位符,名字前带冒号,例如下面这样: name = params[:name]
pay_type = params[:pay_type] pos = Order.find(:all,
:conditions => [\ {:pay_type => pay_type, :name => name}])
我也可以更进一步,因为params是一个hash,我们可以让conditions部分更简单 pos = Order.find(:all,
:conditions => [\
不管使用哪种占位符,Active Record都会很小心地处理SQL中的引号和escape。使用动态SQL,Active Record会保护我们不受SQL注入攻击。
注1:这里的动态sql不同于oracle等数据库中所指的动态sql,其含义类似于ADO.net中不拼接sql字符串,而是传参数来防止sql注入攻击。
现在我们知道如何指定条件,现在我们来看看find方法支持的一些其他设置。
首先,理解find(:first,?)方法是非常重要的,该方法在同样的conditions设置下,和find(:all,?)方法生成同样的sql语句,不同之处仅在于只返回一条记录。在调用的时候,一种的参数为:first,另一种为:all。
find方法执行一个select from这样的sql语句,:all标记指定返回表中所有的记录,:first返回第一条记录。但是现在:first还不能保证你得到的就是表中的第一条记录,原因是什么?我们继续往下看。
:conditions参数指定了SQL语句的where部分,可以包含Sql语句或者包含可以替换的参数的名字,值,上一篇我们已经做了了解。
daves_orders = Order.find(:all, :conditions => \ name = params[:name]
other_orders = Order.find(:all, :conditions => [\ yet_more = Order.find(:all,
:conditions => [\ params])
上面的find方法并不能保证按照特定的顺序返回记录,除非指定查询的排序(order by)部分。:order参数就是用来指定SQL的排序条件的,下面的例子演示了查询名字为Dave的订单,并且按照pay_type,shipped_at字段进行降序排列。 orders = Order.find(:all,
:conditions => \
:order => \
我们还可以设置:limit参数来限制返回的记录数,如果你使用:limit参数,或许还想指定排序条件,下面的例子返回10条记录,并且按照指定条件排序: orders = Order.find(:all,
:conditions => \
:order => \ :limit => 10)
参数:offset经常与:limit参数一同出现,用来指定从第一条记录起,返回指定的偏移量,下面代码演示了:offset参数的使用:
def Order.find_on_page(page_num, page_size) find(:all,
:order => \
:limit => page_size,
:offset => page_num*page_size) end
从上面的代码可以看到,这样使用find的场景就是分页显示数据,用pagesize指定每页的记录数,然后由pagenum*page_size指定从第几条开始提取数据。
参数:join用来指定主表和哪些表进行关联查询,:join参数指定的部分会插入到SQL中,下面的代码演示取得一个所有名为“Programing Ruby”的条目的列表: LineItem.find(:all,
:conditions => \
:joins => \
在后面的内容里,我们还会了解更多的进行表关联查询的方法。
现在,我们在来回头看看:all和:first参数,实际上在使用:first参数时,默认的带有参
数:limit,只不过:limit参数的值为1。如果你要取得最后一条记录,只需要改变:order里的排序方向为降序。
find方法为我们构建了完整的Sql查询,而方法find_by_sql方法则允许我们对Sql有完整的控制,该方法只有一个参数,就是你想要使用的完整的sql语句,下面是示例代码: orders = LineItem.find_by_sql(\ \ \
现在有一个问题了,就是返回的Model对象中都包含有哪些属性呢?我们使用attributes( ), attribute_names( ), and attribute_present?( )方法来确定在Model对象中都包含有哪些属性,第一个返回一个hash,里面是键值对,第二个返回属性名的数组,第三个判断Model对象中是否含有某个属性,例如:
orders = Order.find_by_sql(\ first = orders[0] p first.attributes
p first.attribute_names
p first.attribute_present?(\ 程序的结果:
{\ [\ false
find_by_sql方法也可以用来创建包含有派生(derived)(注1)的Model对象,如果你使用了as XXX 这样的sql来给派生字段一个别名,这个别名会作为Model中的属性名,例如:
items = LineItem.find_by_sql(\ \ \ \
\ li = items[0]
puts \
在find_by_sql方法中,我们一样可以使用占位符来给Sql语句传递参数,例如: Order.find_by_sql([\ params[:amount]])
数据库上最常运行的查询莫过于根据指定条件返回符合的结果集,查询可能是返回所有名字为‘dave’的订单,或者是某个博客上所有标题含Rails的post,在很多其他的框架和程序设计语言中,你需要创建sql来执行查询,Active Record利用了ruby语言所包含的动态能力来做这些事。
例如,我们的Order Model包含了诸如name,email,address这样的属性,我们可以使用这些名字对应的find方法来查询,例如: order = Order.find_by_name(\
orders = Order.find_all_by_name(\ order = Order.find_all_by_email(params['email'])
如果你调用了一个Model类的find_by_或者find_all_by_这样字符串打头的格式的方法,Active Record将它们转换成一个查询器(finder),而将方法后面的字符串作为字段名转换find
方法的参数,例如:
order = Order.find_by_name(\
上面的调用等效的转换为: order = Order.find(:first,
:conditions => [\ other_args...)
类似的,调用一个find_all_by_xxx方法相当于调用find(:all,? )方法。 到这里魔术还没有停止,Active Record还可以对多个列创建查询器(finder),例如,你可以写:
user = User.find_by_name_and_password(name, pw) 相当于:
user = User.find(:first,
:conditions => [\
为了确定要检查哪些字段,Active Record简单的分割find_by_和find_all_by_后面的字符串,这在大多数情况下是够用的,除非你的根本就没有某个在方法名中包含的字段。
注意,Active Record并不提供在find_by_或find_all_by后面的两个字段名中含有_or_。 Active Record提供了两个方法来获取符合条件的记录的条数:count()和count_by_sql()。例如:
c1 = Order.count
c2 = Order.count([\c3 = LineItem.count_by_sql(\\
\\
puts \
在一个程序中,数据库有可能被多个进程或多个程序访问,随时都有可能获取最新的Model对象,这些对象有可能刚刚被编辑过。 从某种程度上讲,这主要应用在事务中,不管怎么说,当你需要手动刷新Model对象时,Active Record可以帮助你,只需调用reload()方法,Model对象属性的值就会被数据库中的值更新。
stock = Market.find_by_ticker(\loop do
puts \sleep 60 stock.reload end
前面了解了检索的方法,这次来看看Active Record怎样更新数据库中的记录。
如果你有一个Active Record对象(或许对应于order表),你可以通过调用save方法将它写道数据库中去,如果这个对象是先前从数据库中读取出来的,save方法将会更新既有的记录,否则将会新建一条记录。 如果一条既有记录被更新,Active Record将会用它的主键和来匹配内存中的对象,Active Record对象中的属性被更新到对应的列,即使一个列中的值没有变化也会被更新,在下面的例子中,id为123的订单所有的内容都会被更新: order = Order.find(123)
共分享92篇相关文档