可排序的 UUID 和覆盖 ActiveRecord::Base

Sortable UUIDs and overriding ActiveRecord::Base

我想在我正在构建的应用程序中使用 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. 如果我想排序并让它正确排序,我 使用不同的方法吗?

这是调味料:

# 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

Rails4.2,Ruby2.2.2

参考:

首先,first and last 并不像您想象的那么简单:您完全忽略了这两种方法都支持的 limit 论点。

其次,scope 只不过是添加用于 return 查询的 class 方法的奇特方式。您的范围正在滥用 scope,因为它们 return 单个模型实例而不是查询。您根本不想使用 scope,您只是想替换 firstlast class 方法,那么为什么不覆盖它们呢?不过,您需要正确地覆盖它们,这将需要阅读和理解 Rails 源代码,以便您正确地模仿 find_nth_with_limit 的作用。您想在使用时覆盖 secondthird、...以及其他那些愚蠢的方法。

如果您觉得替换 firstlast 不合适(IMO 是件好事),那么您可以添加默认范围以根据需要进行排序:

default_scope -> { order(:created_at) }

当然,默认范围有其自身的一系列问题,像这样将东西偷偷放入 ORDER BY 可能会迫使您在任何时候真正想要指定 ORDER BY 时调用 reorder;请记住,多次调用 order 会添加新的订购条件,它们不会替换已经存在的订购条件。

或者,如果您使用的是 Rails6+,则可以使用 来避免默认范围可能导致的所有问题。


我认为你的做法全错了。每当我看到 M.first 时,我都认为有什么东西被遗忘了。按 id 排序几乎没有用,所以你应该 总是 在使用像 firstlast 这样的方法之前手动指定你想要的顺序。

Rails 6(当前版本为 6.0.0rc1)用 implicit_order_column!

来拯救

通过 created_at 订购并使 .first.last.second 等尊重它就像:

class ApplicationRecord < ActiveRecord::Base
  self.implicit_order_column = :created_at
end

id 替换为 uuid 后,我在关联分配外键的方式上遇到了一些奇怪的事情,而不是 .last.first,而是因为我只是忘记使用 uuid 将 default: 'gen_random_uuid()' 添加到其中一个表中。一旦我解决了这个问题,问题就解决了。

create_table :appointments, id: :uuid, default: 'gen_random_uuid()' do |t|