【问题标题】:Adding timestamps to an existing table with Ecto's timestamps使用 Ecto 的时间戳向现有表添加时间戳
【发布时间】:2018-03-27 14:51:04
【问题描述】:

How to add timestamps to an existing table with Ecto's timestamps? 已在此处提出此问题,但接受的解决方案意味着每个新条目都将具有相同的默认时间。我希望新条目具有正确的插入/更新时间。

例如。

# set default to given date to fill in all existing rows with timestamps
def change do
  alter table(:my_table) do
   timestamps(default: "2018-01-01 00:00:01")
  end
end

如果这是迁移中的全部内容,则 每个 inserted_atupdated_at for :my_table 将具有 2018-01-01 00:00:01 作为值,无论插入/更新的日期。

我想做的是:

  1. 为预先存在的行向inserted_at 和updated_at 列添加日期时间。
  2. inserted_atupdated_at 应该是 null: false,因为它们在向新创建的表中添加时间戳时是这样的。
  3. 未来的条目应该具有正确的inserted_at 和 updated_at 值,即inserted_at 是创建行的时间,updated_at 是更改行的时间,而不是迁移中的默认设置。

我有几个解决方案可以实现这一点,但它们看起来很混乱。我正在寻找是否有更清洁的方法来执行此操作,或者是否有处理我遗漏的这种情况的选项。

工作迁移 1:

def up do
  alter table(:my_table) do
    timestamps(default: "now()")
  end
  execute("ALTER TABLE my_table ALTER COLUMN inserted_at SET DEFAULT now()")
  execute("ALTER TABLE my_table ALTER COLUMN updated_at SET DEFAULT now()")
end

def down do
  alter table(:my_table) do
    remove :inserted_at
    remove :updated_at
  end
end

工作迁移 2:

def up do
  alter table(:my_table) do
    timestamps(null: true)
  end
  execute("UPDATE my_table SET inserted_at = now()")
  execute("UPDATE my_table SET updated_at = now()")
  alter table(:my_table) do
    modify :inserted_at, :naive_datetime, null: false
    modify :updated_at, :naive_datetime, null: false
  end
end

def down do
  alter table(:my_table) do
    remove :inserted_at
    remove :updated_at
  end
end

【问题讨论】:

  • 我认为到目前为止您所做的是更新已添加字段的最佳解决方案。
  • 或者您可以使用默认值,并且每次必须将列添加到表中时,您将明确添加插入和更新的字段。
  • 你应该知道,这个功能是故意不支持的,因为 MySQL 不支持 read after writes,所以如果你的 ecto 模型和数据库在运行时可能会不同步。见José's comment here

标签: elixir phoenix-framework ecto


【解决方案1】:

您可以使用fragment 提供一个SQL 函数作为默认值。该文档提供了以下示例:

create table("posts") do
  add :inserted_at, :naive_datetime, default: fragment("now()")
end

timestamps seems to forward the default: option to add,因此在您的具体情况下,您应该能够执行以下操作:

def change do
  alter table(:my_table) do
    timestamps(default: fragment("now()"))
  end
end

【讨论】:

  • 应该注意的是,如果您使用的是 MySQL,这些时间戳可能与运行时数据库和模型中的时间戳不同。请参阅José's comment 关于 read_after_writes
【解决方案2】:

我遇到了同样的问题。对我来说,这是因为没有在 Ecto 架构中指定 timestamps()

schema "my_table" do
    field(:name, :string)
    ...
    timestamps() // <- Add this here
end

迁移只是告诉您的数据库您有时间戳列。你仍然需要告诉 Ecto 它们存在!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-03-18
    • 1970-01-01
    • 2011-11-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-20
    • 2019-10-27
    相关资源
    最近更新 更多