Rails 关于 Rails 中「scope」和「where」的两个疑问

linjunzhugg · 2014年08月17日 · 最后由 tsaikoga 回复于 2014年09月19日 · 5571 次阅读

环境:Rails4.0 + Ruby2.0

##问题 1: 有这么一段代码

class Article < ActiveRecord::Base
    scope :recent, -> { find_by( name: 'rails' }
end

Article.recent

当数据库中存在 name: 'rails' 的记录时,一切正常。 但是当数据中不存在 name: 'rails' 的记录时,返回了 all 数据。

请问是什么原因?

##问题 2:

在 model 里,有个类型为「boolean」的属性。执行语句:

Article.where( home: true )    =>  返回正常返回 home 为 true 的记录)

SQL语句为:  select xxx from xxx where 'article.home' = 'f'

Article.where( home: false)     => 返回不正常(数据库是有 home 为 false 的记录的)但是返回为空。

SQL语句为:  select xxx from xxx where 'article.home' = 't'

Article.where( home: 'false') => 返回正常。
  1. 请问为什么第二步会返回不正常?明明数据库是有 home 为 false 的记录的,却返回为空
  2. 为什么 sql 语句的查询方式有点奇怪,不是应该为 where 'article.home' = true 么?

问题 1 解决:

因为 scope,如果查询结果为 nil 的话,是会返回 all 数据的。

因此如果要返回单个记录的 find_by,where( xxx: xxx).last,最好还是定义 method 吧。

做不出楼主那种奇怪的 SQL

2.1.1 :001 > User.where(admin: true)
  User Load (1.5ms)  SELECT "users".* FROM "users"  WHERE "users"."admin" = 't'
 => #<ActiveRecord::Relation [#<User id: 1, 
2.1.1 :002 > User.where(admin: false)
  User Load (0.3ms)  SELECT "users".* FROM "users"  WHERE "users"."admin" = 'f'
 => #<ActiveRecord::Relation []> 

检查下数据库,是不是搞成 string 了。

Q#1,看源码

def scope(name, body, &block)
   if dangerous_class_method?(name)
     raise ArgumentError, "You tried to define a scope named \"#{name}\" " \
       "on the model \"#{self.name}\", but Active Record already defined " \
       "a class method with the same name."
   end

   extension = Module.new(&block) if block

   singleton_class.send(:define_method, name) do |*args|
     scope = all.scoping { body.call(*args) }
     scope = scope.extending(extension) if extension

     scope || all
   end
 end

会先调用定义的 scope 去查询,如果返回 nil, 会再次执行一次 all 查询,所以应该会看到两条 SQL 查询。

#2 楼 @kepaning 我疑问的就是:SQL 语句中,查询条件为 boolean 类型的,应该是 '属性' = true / false,而在 rails 里,却是 '属性' = 't / f',为什么这里变成字符串了呢?

#3 楼 @small_fish__ 已经确认过,是「boolean」类型

#4 楼 @loveltyoic 恩,问题 1 已经解决。THX。不过感觉这样实现不太好,会出乎 ' 实现者 ' 的意料之外

一切真相大白了:我在 migration 文件中写错了,把 false 写成了 :false,写成了字符串 ( 这样居然不会报错,好吧,sqlite 的原因,mysql 的话直接报错)

sqlite 下:boolean 类型的值,true 为 't' , false 为 'f' 。 mysql 下:boolean 类型的值,true 为 true, false 为 false

sql 语句查询:

sqlite: select xx from xx where 'xx' = ' t / f' mysql: select xx from xx where 'xx' = ' 0 / 1 / true / false ' ( 在 rails 下统一为 0 / 1 )

第一个问题我也遇到,所以有些情况没有使用 scope

需要 登录 后方可回复, 如果你还没有账号请 注册新账号