如何更改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
,我意识到timestamp
是datetime
的别名。
我将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_at
和updated_at
)。 如果您希望任何其他时间戳列相同(即,DB中没有亚秒级精度),请在迁移中指定timestamp0
,它应该可以工作(尽管我还没有测试过)。