如何更改rails migration t.timestamps以在postgres中使用`timestamp(0)without timezone`

我试图找出如何更改t.timestamps在rails迁移中使用的本机数据类型。 最终在postgres中的默认类型是timestamp without timezone 。 我想要的是timestamp(0) without timezone

我想更改本机数据类型,以便在创建新表并在迁移中使用t.timestamps ,它会自动创建正确的时间戳数据类型。

我需要timestamp(0) without timezone因为我的rails应用程序与laravel应用程序共享其数据库,并且两个应用程序都可以插入数据。 由于rails使用毫秒/ laravel的事实没有,并且似乎没有办法(截至2018-10-23)laravel支持包含不同格式的时间戳的表( Ymd H:i:su vs Ymd H:i:s )无需关闭模型中的时间戳,基本上禁用它们的自动管理,我想让数据库强制使用单一格式( Ymd H:i:s )。

有关更多详细信息,请另外提问: 有没有办法将Rails默认时间戳更改为Ymd H:i:s(而不是Ymd H:i:su)或laravel忽略Ymd H的小数部分:i:su?

所以我想使用timestamp(0)来截断毫秒,而不必考虑在创建新表时正确设置表时间戳类型,因为本机类型已经是timestamp(0)

我试过这个

 ./config/environments/initializers require "active_record/connection_adapters/postgresql_adapter" module ActiveRecord module ConnectionAdapters class PostgreSQLAdapter NATIVE_DATABASE_TYPES.merge!( timestamp: { name: "timestamp(0) without timezone" } ) end end end 

和像这样的迁移

 class ChangeTimestampTypesToTimestamp0  { "gen_random_uuid()" } do|t| t.string :name, null: false t.timestamps end end end 

但那没用。

我还试图改变时间戳使用timestampz与上面相同的迁移作为一个完整性检查,仍然没有运气…

 require "active_record/connection_adapters/postgresql_adapter" module ActiveRecord module ConnectionAdapters class PostgreSQLAdapter NATIVE_DATABASE_TYPES.merge!( timestamp: { name: "timestamptz" } ) end end end 

在此处输入图像描述

我相信我已经明白了!

我开始通过从控制台打印出变量来查看NATIVE_DATABASE_TYPES带来的设置

 Rails c ActiveRecord::ConnectionAdapters::PostgreSQLAdapter::NATIVE_DATABASE_TYPES 

结果: {:primary_key=>"bigserial primary key", :string=>{:name=>"character varying"}, :text=>{:name=>"text"}, :integer=>{:name=>"integer", :limit=>4}, :float=>{:name=>"float"}, :decimal=>{:name=>"decimal"}, :datetime=>{:name=>"timestamp"}, :time=>{:name=>"time"}, :date=>{:name=>"date"}, :daterange=>{:name=>"daterange"}, :numrange=>{:name=>"numrange"}, :tsrange=>{:name=>"tsrange"}, :tstzrange=>{:name=>"tstzrange"}, :int4range=>{:name=>"int4range"}, :int8range=>{:name=>"int8range"}, :binary=>{:name=>"bytea"}, :boolean=>{:name=>"boolean"}, :xml=>{:name=>"xml"}, :tsvector=>{:name=>"tsvector"}, :hstore=>{:name=>"hstore"}, :inet=>{:name=>"inet"}, :cidr=>{:name=>"cidr"}, :macaddr=>{:name=>"macaddr"}, :uuid=>{:name=>"uuid"}, :json=>{:name=>"json"}, :jsonb=>{:name=>"jsonb"}, :ltree=>{:name=>"ltree"}, :citext=>{:name=>"citext"}, :point=>{:name=>"point"}, :line=>{:name=>"line"}, :lseg=>{:name=>"lseg"}, :box=>{:name=>"box"}, :path=>{:name=>"path"}, :polygon=>{:name=>"polygon"}, :circle=>{:name=>"circle"}, :bit=>{:name=>"bit"}, :bit_varying=>{:name=>"bit varying"}, :money=>{:name=>"money"}, :interval=>{:name=>"interval"}, :oid=>{:name=>"oid"} {:primary_key=>"bigserial primary key", :string=>{:name=>"character varying"}, :text=>{:name=>"text"}, :integer=>{:name=>"integer", :limit=>4}, :float=>{:name=>"float"}, :decimal=>{:name=>"decimal"}, :datetime=>{:name=>"timestamp"}, :time=>{:name=>"time"}, :date=>{:name=>"date"}, :daterange=>{:name=>"daterange"}, :numrange=>{:name=>"numrange"}, :tsrange=>{:name=>"tsrange"}, :tstzrange=>{:name=>"tstzrange"}, :int4range=>{:name=>"int4range"}, :int8range=>{:name=>"int8range"}, :binary=>{:name=>"bytea"}, :boolean=>{:name=>"boolean"}, :xml=>{:name=>"xml"}, :tsvector=>{:name=>"tsvector"}, :hstore=>{:name=>"hstore"}, :inet=>{:name=>"inet"}, :cidr=>{:name=>"cidr"}, :macaddr=>{:name=>"macaddr"}, :uuid=>{:name=>"uuid"}, :json=>{:name=>"json"}, :jsonb=>{:name=>"jsonb"}, :ltree=>{:name=>"ltree"}, :citext=>{:name=>"citext"}, :point=>{:name=>"point"}, :line=>{:name=>"line"}, :lseg=>{:name=>"lseg"}, :box=>{:name=>"box"}, :path=>{:name=>"path"}, :polygon=>{:name=>"polygon"}, :circle=>{:name=>"circle"}, :bit=>{:name=>"bit"}, :bit_varying=>{:name=>"bit varying"}, :money=>{:name=>"money"}, :interval=>{:name=>"interval"}, :oid=>{:name=>"oid"}

事实certificate,在我开始将timestamp包含在我之前, timestamp实际上从未实际设置过

 module ActiveRecord module ConnectionAdapters class PostgreSQLAdapter NATIVE_DATABASE_TYPES.merge!( timestamp: { name: "timestamp", limit:0 } ) end end end 

包含的内容是datetime ,我意识到timestampdatetime的别名。

我将NATIVE_DATABASE_TYPES合并更改为这样……

 require "active_record/connection_adapters/postgresql_adapter" module ActiveRecord module ConnectionAdapters class PostgreSQLAdapter NATIVE_DATABASE_TYPES.merge!( datetime: { name: "timestamp", limit:0 } ) end end end 

我运行了我的迁移,并且列成功设置为timestamp(0) without timezone

这是一个解决方案。 它改变了Rails中的默认时间戳精度,包括迁移,PostgreSQL中两个时间戳到一秒精度。 它既不简单也不简单,但适用于带有PostgreSQL的Rails 5.2。

我认为初始化程序应该放在config/initializers/ (不在environments )。
写下面的文件。

 # ./config/initializers/arbitrary.rb require "active_record/connection_adapters/abstract/schema_definitions.rb" require "active_record/connection_adapters/abstract_adapter" require "active_record/connection_adapters/abstract/schema_statements" require "active_record/connection_adapters/postgresql/schema_statements" require "active_record/connection_adapters/postgresql_adapter" module ActiveRecord module ConnectionAdapters # Overwrites a method in /abstract/schema_definitions.rb class TableDefinition def timestamps(**options) options[:null] = false if options[:null].nil? column(:created_at, :datetime0, options) column(:updated_at, :datetime0, options) end end # Overwrites a method in /abstract/schema_statements.rb module SchemaStatements def add_timestamps(table_name, options = {}) options[:null] = false if options[:null].nil? add_column table_name, :created_at, :datetime0, options add_column table_name, :updated_at, :datetime0, options end end # Overwrites a method in /postgresql/schema_statements.rb module PostgreSQL module SchemaStatements def add_timestamps_for_alter(table_name, options = {}) [add_column_for_alter(table_name, :created_at, :datetime0, options), add_column_for_alter(table_name, :updated_at, :datetime0, options)] end end end # Modifies a constant and methods in /postgresql_adapter.rb class PostgreSQLAdapter alias_method :initialize_type_map_orig, :initialize_type_map if ! self.method_defined?(:initialize_type_map_orig) NATIVE_DATABASE_TYPES[:datetime0] = { name: "timestamp(0)" } private def initialize_type_map(m = type_map) register_class_with_precision_t0 m, "timestamp0", OID::DateTime initialize_type_map_orig(m) end def register_class_with_precision_t0(mapping, key, klass) mapping.register_type(key) do |*args| klass.new(precision: 0) end end end end end 

这是一个示例迁移文件。

 # db/migrate/20181023182238_create_articles.rb class CreateArticles < ActiveRecord::Migration[5.2] def change create_table :articles do |t| t.string :title t.timestamps end end end 

迁移( bin/rails db:migrate )在PostgreSQL数据库中创建一个包含timestamp(0) (无时区)的两个时间戳列的表articles

执行的SQL是这样的:

 CREATE TABLE "articles" ( "id" bigserial primary key, "title" character varying, "created_at" timestamp(0) NOT NULL, "updated_at" timestamp(0) NOT NULL); 

注意

我已确认迁移以创建表并在Raisl控制台中进行数据更新。 它也意味着在mingration中工作以更新表,但我还没有测试它。

稍微调整一下,它也适用于其他数据库。

基本上,上面的代码定义了一个新的Rails类型timestamp0 ,分配了timestamps()created_atupdated_at )。 如果您希望任何其他时间戳列相同(即,DB中没有亚秒级精度),请在迁移中指定timestamp0 ,它应该可以工作(尽管我还没有测试过)。