如何在Ruby on Rails 5.2中使用ActiveRecord迁移创建表时定义序列?

4
我需要将一个特定的Postgres序列分配给我的表的ID字段。在模型中,我尝试定义以下设置,但对Posgres没有任何影响:

class MyObject < ActiveRecord::Base

self.sequence_name = "global_seq"

通常,在ActiveRecord migrations中,表定义从以下内容开始:
create_table "objects", id: :serial, force: :cascade do |t|

生成Postgres列默认值定义的方法如下:
default nextval('objects_id_seq'::regclass)

我该如何在迁移中指定nextval()函数依赖于另一个序列?
3个回答

7

您可以在迁移中更改默认设置:

change_column :my_objects, :id, :integer, default: -> { "nextval('global_seq')" }

根据您的序列和表格设置,您可能希望使用:bigint代替:integer。为了将原始的nextval('global_seq')表达式输入数据库,您必须使用lambda作为:default选项。

您可能还希望删除旧的序列,据我所知,您需要使用connection.execute('drop sequence ...')来完成。

如果您在create_table中跳过默认的:id步骤,则可以在手动创建:id列时一次性完成所有操作:

create_table :my_objects, id: false do |t|
  t.bigint :id, null: false, default: -> { "nextval('global_seq')" }
  t.primary_key :id
  ...
end

再次强调,选择t.bigintt.integer取决于您希望主键有多大。


解决方案很聪明,但是在使用Rails 5.1.6时,create_table :my_objects, id: false仍然会创建并引用传统序列。我必须无论如何应用change_column。 - user1185081
你确定吗?我认为Rails根本不会创建序列,它只是创建一个serialbigserial列(取决于版本),然后让PostgreSQL创建序列并设置其所有权。 - mu is too short
你是正确的,就像这里提到的一样:https://www.postgresql.org/docs/9.5/static/datatype-numeric.html#DATATYPE-SERIAL。我的意思是,选项"id: false"并没有避免创建带有序列数据类型的id列。或者primary_key数据类型生成了这个唯一索引... - user1185081
实际上,t.primary_key :id 语句负责创建传统序列。我将其删除了,现在它可以工作了。 - user1185081
但是,这样您不是必须使用一些SQL手动设置主键吗? - mu is too short
是的,蛇咬自己的尾巴...我会深入调查并尽快更新帖子。感谢您的帮助! - user1185081

4
我很抱歉,Rails的迁移没有内置命令可以显式地为列设置序列,这是非常具体于数据库的。但是,可以使用普通的SQL语句来完成:
class ChangeSequenceOfObjectId < ActiveRecord::Migration[5.0]
  def self.up
    execute <<-SQL
      CREATE SEQUENCE global_seq;
      ALTER TABLE objects ALTER COLUMN id SET DEFAULT nextval('global_seq');
      ALTER SEQUENCE objects_id_seq OWNED BY NONE;
      ALTER SEQUENCE global_seq OWNED BY objects.id;
    SQL
  end

  def self.down
    execute <<-SQL
      ALTER TABLE objects ALTER COLUMN id SET DEFAULT nextval('objects_id_seq');
      ALTER SEQUENCE objects_id_seq OWNED BY objects.id;
      ALTER SEQUENCE global_seq OWNED BY NONE;
      DROP SEQUENCE global_seq;
    SQL
  end

如果global_seq序列是在其他迁移中创建的,那么只需删除关于其创建/删除的相应行。

此外,如果您想将global_sql序列保留为“独立”的(即使objects表被删除,它仍然存在于数据库中),则可以删除ALTER SEQUENCE global_seq OWNED BY命令。


如果您使用lambda表达式来设置“:default”选项(同样适用于在“created_at”和“updated_at”上设置合理的默认值),则可以轻松完成此操作。无论如何,这种做法涉及到许多巧妙的技巧,因为Rails确实喜欢按照自己的方式进行事情。 - mu is too short
哇,我从没以这种方式使用过default选项。不错的建议。 - Ilya Konyukhov
由于紧随其后的primary_key指令,:default未能正常工作。这在Rails 4.2中也是有效的。 - hoitomt

1

我使用sequence_name选项(Rails 5.1):

create_table "opjects", sequence_name: 'global_seq' do |t|

我没有检查,但我猜在Rails 5.2中它也应该可以工作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接