可排序的 UUID 和重写 ActiveRecord::Base

问题描述 投票:0回答:4

我想在我正在构建的应用程序中使用 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

参考:

ruby-on-rails postgresql ruby-on-rails-4 activerecord uuid
4个回答
6
投票

Rails 6(当前版本为 6.0.0rc1)通过 implicit_order_column

来拯救

通过

created_at
订购并制作
.first
.last
.second
等尊重它就像简单:

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

5
投票

首先,

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
等方法之前,您应该
始终
手动指定所需的顺序。


0
投票

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

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

0
投票

根据here链接的rails文档执行此操作的最佳方法是确保为您想要使用的UUID添加默认选项。关于选择 UUID 的好文章链接到here。我使用 ULID,因为它们是可排序的,并且通过将我的 UUID 选项指定为 ULID,我的记录是可排序的!

© www.soinside.com 2019 - 2024. All rights reserved.