我想在我的模型中指定一个默认排序顺序。
因此,当我执行.where()而不指定.order()时,它使用默认排序。但是如果我指定了.order(),它将覆盖默认值。
我想在我的模型中指定一个默认排序顺序。
因此,当我执行.where()而不指定.order()时,它使用默认排序。但是如果我指定了.order(),它将覆盖默认值。
当前回答
公认的答案过去是正确的——默认作用域是做到这一点的唯一方法。然而,默认作用域实际上是链式的(作用域本身应用于任何其他作用域/调用),这可能会导致一些不可预知的行为。因此,默认作用域被广泛认为是要不惜一切代价避免的东西。
It's important to note that given your original question, default scope doesn't actually quite satisfy this, since in order to actually skip the default scope, any model has to explicitly specify .unscoped first. That means that if order(:something) is called, without .unscoped, the result is actually more similar to order(:default).order(:something). :something takes priority, sure, but :default is still there, so now there's a multicolumn sort, which might not be desired. The original question specifies that the default sort order be ignored if another order is called, and default scope, without .unscoped, doesn't meet that requirement.
但是,自从Rails 6以来,现在有了implicit_order_column,它可以在模型上设置。
class Book < ApplicationRecord
self.implicit_order_column = 'publish_date'
end
这将导致模型默认按该列排序,而不是按主键排序。与默认的内置排序(使用id)一样,但是,如果指定了不同的顺序(使用order(:something)),则忽略此隐式排序,而不是链式排序:.order(:something)不会导致多列排序,因此不需要.unscoped。
它最常见的用途之一是切换到UUID作为主键。默认情况下,Rails仍然会对主键进行排序,但是由于该键现在是一个没有意义的字节字符串,因此这个顺序现在同样没有意义。因此要复制旧的行为,例如SomeModel。默认情况下,last应该返回最近创建的记录,只需在ApplicationRecord上设置这个:
class ApplicationRecord < ActiveRecord::Base
self.implicit_order_column = 'created_at'
end
Note that it is theoretically possible to cause an error here. created_at is not unique - it's stored with microsecond precision, but in theory it's possible to create to records with the exact same created_at time, and thus, any returned results that depended only on that would be non-deterministic - subsequent calls might return different records. It is a low enough chance, however, that it's often considered safe to do this - particularly if the code does not depend on a deterministic result here (which, it probably shouldn't - if deterministic results are needed, referencing the primary key or some other unique column is better when possible).
同样值得注意的是,默认情况下,created_at(或使用的任何列)没有键。这意味着select操作会稍微慢一些,至少在将一个键添加到该列以修复此问题之前。
最后,implicit_order_column的功能有限——它甚至不能设置排序顺序,更不用说执行更复杂的事情,比如多列排序。如果需要该功能,那么default_scope仍然是唯一的选择。
其他回答
快速更新一下上面迈克尔的精彩回答。
对于Rails 4.0+,你需要像这样把你的排序放在一个块中:
class Book < ActiveRecord::Base
default_scope { order('created_at DESC') }
end
注意,order语句被放置在花括号表示的块中。
他们之所以改变它,是因为它太容易传入动态的东西(比如当前时间)。这消除了问题,因为块是在运行时计算的。如果你不使用block,你会得到这样的错误:
取消了不带块调用#default_scope的支持。例如,请使用default_scope {where(color: 'red')}代替default_scope where(color: 'red')。(或者你也可以重新定义self.default_scope。)
正如@Dan在下面的评论中提到的,你可以使用更ruby的语法,像这样:
class Book < ActiveRecord::Base
default_scope { order(created_at: :desc) }
end
或者有多个列:
class Book < ActiveRecord::Base
default_scope { order({begin_date: :desc}, :name) }
end
谢谢@Dan !
default_scope
这适用于Rails 4+:
class Book < ActiveRecord::Base
default_scope { order(created_at: :desc) }
end
对于Rails 2.3, 3,你需要这样做:
default_scope order('created_at DESC')
对于Rails 2.x:
default_scope :order => 'created_at DESC'
其中created_at是您希望对其执行默认排序的字段。
注意:ASC是升序代码,DESC是降序代码(DESC,不是dsc !)
范围
一旦你习惯了,你也可以使用scope:
class Book < ActiveRecord::Base
scope :confirmed, :conditions => { :confirmed => true }
scope :published, :conditions => { :published => true }
end
对于Rails 2,您需要named_scope。
:published scope给你Book。出版而不是 书。Find (:published => true)。
从Rails 3开始,你可以将这些方法用句点连接起来,这样你就可以使用Book.published.confirmed了。
使用这种方法,直到需要实际结果时才实际执行查询(惰性求值),因此可以将7个作用域链接在一起,但只产生1个实际的数据库查询,以避免执行7个独立查询带来的性能问题。
你可以使用一个传入的参数,如日期或user_id(这些参数在运行时会改变,因此需要“惰性求值”,使用lambda,如下所示:
scope :recent_books, lambda
{ |since_when| where("created_at >= ?", since_when) }
# Note the `where` is making use of AREL syntax added in Rails 3.
最后,你可以禁用默认作用域:
Book.with_exclusive_scope { find(:all) }
或者更好:
Book.unscoped.all
它将禁用任何筛选(条件)或排序(排序)。
请注意,第一个版本适用于Rails2+,而第二个(无作用域)仅适用于Rails3+
所以 ... 如果你在想,嗯,这些就像方法,那么……是的,这正是这些瞄准镜的作用! 他们就像拥有自我一样。method_name代码……结束,但与ruby一样,它们是很好的语法捷径(或“糖”),让事情变得更容易!
事实上,它们是类级方法,因为它们操作的是1组“所有”记录。
然而,它们的格式正在改变,在rails 4中,当使用#scope而不传递可调用对象时,会出现弃用警告。例如scope:red, where(color: 'red')应该改为scope:red, -> {where(color: 'red')}。
顺便说一句,如果使用不当,default_scope可能会被误用/滥用。 这主要是关于它何时用于限制(过滤)默认选择(默认是一个坏主意)等操作,而不仅仅是用于排序结果。 对于where选择,只需使用常规命名作用域。并在查询中添加该作用域,例如Book.all.published,其中published是命名作用域。
总之,作用域真的很棒,可以帮助你把东西推到“胖模型瘦控制器”dry方法的模型中。
公认的答案过去是正确的——默认作用域是做到这一点的唯一方法。然而,默认作用域实际上是链式的(作用域本身应用于任何其他作用域/调用),这可能会导致一些不可预知的行为。因此,默认作用域被广泛认为是要不惜一切代价避免的东西。
It's important to note that given your original question, default scope doesn't actually quite satisfy this, since in order to actually skip the default scope, any model has to explicitly specify .unscoped first. That means that if order(:something) is called, without .unscoped, the result is actually more similar to order(:default).order(:something). :something takes priority, sure, but :default is still there, so now there's a multicolumn sort, which might not be desired. The original question specifies that the default sort order be ignored if another order is called, and default scope, without .unscoped, doesn't meet that requirement.
但是,自从Rails 6以来,现在有了implicit_order_column,它可以在模型上设置。
class Book < ApplicationRecord
self.implicit_order_column = 'publish_date'
end
这将导致模型默认按该列排序,而不是按主键排序。与默认的内置排序(使用id)一样,但是,如果指定了不同的顺序(使用order(:something)),则忽略此隐式排序,而不是链式排序:.order(:something)不会导致多列排序,因此不需要.unscoped。
它最常见的用途之一是切换到UUID作为主键。默认情况下,Rails仍然会对主键进行排序,但是由于该键现在是一个没有意义的字节字符串,因此这个顺序现在同样没有意义。因此要复制旧的行为,例如SomeModel。默认情况下,last应该返回最近创建的记录,只需在ApplicationRecord上设置这个:
class ApplicationRecord < ActiveRecord::Base
self.implicit_order_column = 'created_at'
end
Note that it is theoretically possible to cause an error here. created_at is not unique - it's stored with microsecond precision, but in theory it's possible to create to records with the exact same created_at time, and thus, any returned results that depended only on that would be non-deterministic - subsequent calls might return different records. It is a low enough chance, however, that it's often considered safe to do this - particularly if the code does not depend on a deterministic result here (which, it probably shouldn't - if deterministic results are needed, referencing the primary key or some other unique column is better when possible).
同样值得注意的是,默认情况下,created_at(或使用的任何列)没有键。这意味着select操作会稍微慢一些,至少在将一个键添加到该列以修复此问题之前。
最后,implicit_order_column的功能有限——它甚至不能设置排序顺序,更不用说执行更复杂的事情,比如多列排序。如果需要该功能,那么default_scope仍然是唯一的选择。
可以使用default_scope实现默认排序顺序 http://api.rubyonrails.org/classes/ActiveRecord/Scoping/Default/ClassMethods.html