Rails在内部将范围转换为类方法,那么为什么我们不能自己使用类方法而不是使用范围。
答案 0 :(得分:10)
来自fine guide:
14范围
[...]
要定义一个简单的作用域,我们在类中使用scope
方法,传递我们想要在调用此作用域时运行的查询:class Article < ActiveRecord::Base scope :published, -> { where(published: true) } end
这与定义类方法完全相同,您使用的是个人偏好:
class Article < ActiveRecord::Base def self.published where(published: true) end end
特别注意:
这与定义类方法完全相同,您使用的是个人偏好
还有little further(Rails3指南在BTW中说同样的事情):
14.1传递参数
[...]
使用类方法是接受范围参数的首选方法。
所以你使用它是一个偏好的问题,甚至建议你对带参数的作用域使用类方法。
使用scope
主要是一个符号问题。如果你说scope :whatever
,那么你明确地说whatever
是一个查询构建者;如果您说def self.whatever
,那么您并未暗示whatever
方法的意图,那么您只需定义一些可能会或可能不会像范围一样的类方法。< / p>
当然, 14.1 通过建议在范围采用参数时不使用scope
来弄清楚这种符号的区别。还要记住,在Rails3中你可以说:
scope :published, where(published: true)
所以一个无争议的范围在视觉上是清洁的&#34;并且简洁但添加一个lambda来处理参数会使它看起来更混乱:
scope :pancakes, ->(x) { where(things: x) }
但是Rails4甚至想要无争议的范围内的lambdas,这种区别现在更不合理了。
我怀疑此时的差异是历史性的。范围在之前的时代可能是一些特殊的东西,但在Rails3时代成为普通的旧类方法,以减少重复并更好地与Rails3附带的新查询接口进行网格化。
因此,如果您愿意,可以跳过scope
并直接进入课程方法。当您的范围需要参数时,甚至鼓励您这样做。
答案 1 :(得分:1)
Scopes
只是class methods.
内部活动记录将范围转换为类方法。
“他们之间没有区别”或“这是品味的问题”。我倾向于同意这两个句子,但我想表明两者之间存在一些细微差别。 这blogs很好地解释了差异。
答案 2 :(得分:0)
这篇文章最适合我,它很容易解释
http://blog.plataformatec.com.br/2013/02/active-record-scopes-vs-class-methods/
答案 3 :(得分:-5)
为什么我应该使用范围,如果它只是类方法的语法糖?“。所以这里有一些有趣的例子供您考虑。
范围始终是可链接的=&gt; •••••••••••••••••••••••••••••••••••••••••••• 让我们使用以下场景:用户将能够按状态过滤帖子,按最新更新的顺序排序。很简单,让我们写下范围:
class Post < ActiveRecord::Base
scope :by_status, -> status { where(status: status) }
scope :recent, -> { order("posts.updated_at DESC") }
end
And we can call them freely like this:
Post.by_status('published').recent
# SELECT "posts".* FROM "posts" WHERE "posts"."status" = 'published'
# ORDER BY posts.updated_at DESC
Or with a user provided param:
Post.by_status(params[:status]).recent
# SELECT "posts".* FROM "posts" WHERE "posts"."status" = 'published'
# ORDER BY posts.updated_at DESC
So far, so good. Now lets move them to class methods, just for the sake of comparing:
class Post < ActiveRecord::Base
def self.by_status(status)
where(status: status)
end
def self.recent
order("posts.updated_at DESC")
end
end
除了使用一些额外的线路,没有大的改进。但是现在如果:status参数为nil或空白会发生什么?
Post.by_status(nil).recent
# SELECT "posts".* FROM "posts" WHERE "posts"."status" IS NULL
# ORDER BY posts.updated_at DESC
Post.by_status('').recent
# SELECT "posts".* FROM "posts" WHERE "posts"."status" = ''
# ORDER BY posts.updated_at DESC
Oooops, I don't think we wanted to allow these queries, did we? With scopes, we can easily fix that by adding a presence condition to our scope:
scope :by_status, -> status { where(status: status) if status.present? }
There we go:
Post.by_status(nil).recent
# SELECT "posts".* FROM "posts" ORDER BY posts.updated_at DESC
Post.by_status('').recent
# SELECT "posts".* FROM "posts" ORDER BY posts.updated_at DESC
Awesome. Now lets try to do the same with our beloved class method:
class Post < ActiveRecord::Base
def self.by_status(status)
where(status: status) if status.present?
end
end
Running this:
Post.by_status('').recent
NoMethodError: undefined method `recent' for nil:NilClass
And . The difference is that a scope will always return a relation, whereas our simple class method implementation will not. The class method should look like this instead:
def self.by_status(status)
if status.present?
where(status: status)
else
all
end
end
请注意,我将返回所有nil / blank case,它在Rails 4中返回一个关系(它先前从数据库返回了一个项目数组)。在Rails 3.2.x中,您应该使用作用域。我们去了:
Post.by_status('').recent
# SELECT "posts".* FROM "posts" ORDER BY posts.updated_at DESC
所以这里的建议是:永远不要从类应该像作用域那样工作的类方法返回nil,否则你就会破坏作用域隐含的可链接性条件,它总是返回一个关系。
范围是可扩展的=&gt; ••••••••••••••••••••••••••••• 让我们作为下一个例子进行分页,我将使用kaminari gem作为基础。在对集合进行分页时,您需要做的最重要的事情是告诉您要获取哪个页面:
Post.page(2)
After doing that you might want to say how many records per page you want:
Post.page(2).per(15)
And you may to know the total number of pages, or whether you are in the first or last page:
posts = Post.page(2)
posts.total_pages # => 2
posts.first_page? # => false
posts.last_page? # => true
当我们按此顺序调用这些内容时,这一切都有意义,但是在没有分页的集合中调用这些方法没有任何意义,是吗?编写范围时,可以添加仅在对象中可用的特定扩展(如果调用该范围)。对于kaminari,它只将页面范围添加到Active Record模型,并依赖范围扩展功能在调用页面时添加所有其他功能。从概念上讲,代码看起来像这样:
scope :page, -> num { # some limit + offset logic here for pagination } do
def per(num)
# more logic here
end
def total_pages
# some more here
end
def first_page?
# and a bit more
end
def last_page?
# and so on
end
end
范围扩展是我们工具链中强大而灵活的技术。但是,当然,我们总是可以疯狂地通过类方法获得所有这些:
def self.page(num)
scope = # some limit + offset logic here for pagination
scope.extend PaginationExtensions
scope
end
module PaginationExtensions
def per(num)
# more logic here
end
def total_pages
# some more here
end
def first_page?
# and a bit more
end
def last_page?
# and so on
end
end
它比使用示波器更冗长,但它会产生相同的结果。这里的建议是:选择哪种方法更适合您,但确保在重新发明轮子之前知道框架提供了什么。