我想在我正在构建的应用程序中使用 UUID,但遇到了一些问题。由于 UUID(v4)无法排序,因为它们是随机生成的,我试图覆盖 ActiveRecord::Base#first,但 Rails 对此不太满意。它对我大喊
ArgumentError: You tried to define a scope named "first" on the model "Item", but Active Record already defined a class method with the same name.
如果我想排序并使其正确排序,我是否 have 使用不同的方法?
这是酱汁:
# lib/sortable_uuid.rb
module SortableUUID
def self.included(base)
base.class_eval do
scope :first, -> { order("created_at").first }
scope :last, -> { order("created_at DESC").first }
end
end
end
# app/models/item.rb
class Item < ActiveRecord::Base
include SortableUUID
end
Rails 4.2、Ruby 2.2.2
参考:
Rails 6(当前版本为 6.0.0rc1)通过 implicit_order_column!
来拯救通过
created_at
订购并制作.first
、.last
、.second
等尊重它就像简单:
class ApplicationRecord < ActiveRecord::Base
self.implicit_order_column = :created_at
end
first
和last
并不像您想象的那么简单:您完全忽略了这两种方法都支持的limit
论点。
其次,
scope
只不过是添加旨在返回查询的类方法的一种奇特方式。您的范围正在滥用 scope
,因为它们返回单个模型实例而不是查询。您根本不想使用 scope
,您只是想替换 first
和 last
类方法,那么为什么不重写它们呢?不过,您需要正确地覆盖它们,这需要阅读和理解 Rails 源代码,以便正确模仿 find_nth_with_limit
的作用。当你使用这些方法时,你会想要重写 second
、third
...以及其他愚蠢的方法。
如果您对替换
first
和 last
感觉不合适(我认为这是一件好事),那么您可以添加默认范围来根据需要排序:
default_scope -> { order(:created_at) }
当然,默认作用域有自己的一系列问题,像这样将东西偷偷地放入 ORDER BY 中可能会迫使您在任何时候真正想要指定 ORDER BY 时调用
reorder
;请记住,多次调用 order
会添加新的订购条件,它们不会替换已有的条件。
或者,如果您使用的是 Rails6+,则可以使用 Markus 的
implicit_order_column
解决方案 来避免默认范围可能导致的所有问题。
我认为你的想法都是错误的。每当我看到
M.first
时,我都会认为有些事情已经被遗忘了。通过 id
排序几乎没有用,因此在使用 first
和 last
等方法之前,您应该 始终手动指定所需的顺序。
将
id
替换为 uuid
后,我在关联分配外键的方式上遇到了一些奇怪的情况,这不是 .last
和 .first
,而是因为我只是忘记将 default: 'gen_random_uuid()'
添加到使用 uuid 的表之一。一旦我解决了这个问题,问题就解决了。
create_table :appointments, id: :uuid, default: 'gen_random_uuid()' do |t|