可排序的 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
,您只是想替换 first
和 last
class 方法,那么为什么不覆盖它们呢?不过,您需要正确地覆盖它们,这将需要阅读和理解 Rails 源代码,以便您正确地模仿 find_nth_with_limit
的作用。您想在使用时覆盖 second
、third
、...以及其他那些愚蠢的方法。
如果您觉得替换 first
和 last
不合适(IMO 是件好事),那么您可以添加默认范围以根据需要进行排序:
default_scope -> { order(:created_at) }
当然,默认范围有其自身的一系列问题,像这样将东西偷偷放入 ORDER BY 可能会迫使您在任何时候真正想要指定 ORDER BY 时调用 reorder
;请记住,多次调用 order
会添加新的订购条件,它们不会替换已经存在的订购条件。
或者,如果您使用的是 Rails6+,则可以使用 来避免默认范围可能导致的所有问题。
我认为你的做法全错了。每当我看到 M.first
时,我都认为有什么东西被遗忘了。按 id
排序几乎没有用,所以你应该 总是 在使用像 first
和 last
这样的方法之前手动指定你想要的顺序。
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|
我想在我正在构建的应用程序中使用 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
,您只是想替换 first
和 last
class 方法,那么为什么不覆盖它们呢?不过,您需要正确地覆盖它们,这将需要阅读和理解 Rails 源代码,以便您正确地模仿 find_nth_with_limit
的作用。您想在使用时覆盖 second
、third
、...以及其他那些愚蠢的方法。
如果您觉得替换 first
和 last
不合适(IMO 是件好事),那么您可以添加默认范围以根据需要进行排序:
default_scope -> { order(:created_at) }
当然,默认范围有其自身的一系列问题,像这样将东西偷偷放入 ORDER BY 可能会迫使您在任何时候真正想要指定 ORDER BY 时调用 reorder
;请记住,多次调用 order
会添加新的订购条件,它们不会替换已经存在的订购条件。
或者,如果您使用的是 Rails6+,则可以使用
我认为你的做法全错了。每当我看到 M.first
时,我都认为有什么东西被遗忘了。按 id
排序几乎没有用,所以你应该 总是 在使用像 first
和 last
这样的方法之前手动指定你想要的顺序。
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|