我试图弄清楚如何更改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支持具有包含不同时间戳格式的表({{1} } vs Y-m-d H:i:s.u
),而不必关闭模型中的时间戳,本质上是禁用它们的自动管理,我想让数据库强制使用单一格式(Y-m-d H:i:s
)。
有关更多详细信息,请问我另一个问题:Is there a way to change Rails default timestamps to Y-m-d H:i:s (instead of Y-m-d H:i:s.u) or have laravel ignore decimal portion of Y-m-d H:i:s.u?
因此,我想使用Y-m-d H:i:s
截断毫秒数,而不必在创建新表时考虑正确设置表的时间戳类型,因为本机类型已经是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
但是没有用。
我还尝试将时间戳更改为使用timestampz,并将其与正常检查相同地进行迁移,但还是没有运气...
class ChangeTimestampTypesToTimestamp0 < ActiveRecord::Migration[5.2]
def change
create_table :test, id: :uuid, default: -> { "gen_random_uuid()" } do|t|
t.string :name, null: false
t.timestamps
end
end
end
答案 0 :(得分:1)
我相信我已经弄清楚了!
我开始研究通过从控制台打印出变量来设置哪些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"}
事实证明,在我开始将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
答案 1 :(得分:1)
这是一个解决方案。它将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
)创建一个表articles
,该表具有PostgreSQL数据库中timestamp(0)
的两个时间戳列(无时区)。
执行的SQL是这样的:
CREATE TABLE "articles" (
"id" bigserial primary key,
"title" character varying,
"created_at" timestamp(0) NOT NULL,
"updated_at" timestamp(0) NOT NULL);
我已确认在Raisl控制台中进行迁移以创建表和进行数据更新的工作。它也意在更新表,但我尚未对其进行测试。
稍作调整,它也可以在其他数据库中工作。
基本上,以上代码定义了一个新的Rails类型timestamp0
,并为其分配了timestamps()
(分别为created_at
和updated_at
)。
如果您希望其他任何时间戳记列都相同(即,数据库中没有亚秒级精度),请在迁移中指定timestamp0
,它应该可以工作(尽管我尚未测试)。>