【问题标题】:How to add timestamps to an existing table with Ecto's timestamps?如何使用 Ecto 的时间戳向现有表添加时间戳?
【发布时间】:2016-03-02 10:22:43
【问题描述】:

由于inserted_atupdated_at 不能是null,这将不起作用:

def change do
  alter table(:channels) do
    timestamps
  end
end

** (Postgrex.Error) ERROR (not_null_violation): column "inserted_at" contains null values

有没有不复制timestamps'功能的简单方法?

【问题讨论】:

    标签: elixir ecto


    【解决方案1】:

    timestamps/1 函数接受一个选项关键字列表,你可以用它设置默认值。

    def change do
      alter table(:channels) do
        timestamps default: "2016-01-01 00:00:01", null: false
      end
    end
    


    更新 Ecto >= 2.1
    您需要使用新类型 NaiveDateTime

    def change do
      alter table(:channels) do
        timestamps default: ~N[2017-01-01 00:00:01], null: false
      end
    end
    

    如果您有更多疑问,请查看documentation

    【讨论】:

    • Ecto 2.0 已删除此选项 :( 最好的解决方案可能是手动添加这两个字段。
    • 原来的解决方案刚刚在 Ecto 3.2 中工作 :) ... timestamps default: "2016-01-01 00:00:01", null: false -- 这是原始未更新的答案。
    • 我发现的问题是所有未来的inserted_atupdated_at 值都是那个日期。理想情况下,它将为预先存在的行和新行/更新设置一个值,它将使用更新时间。我针对这种情况提出了一个单独的问题here
    • 这看起来很愚蠢?如果您不设置时间戳字段,它将默认为该静态日期?这肯定会在沿线的某个地方造成严重破坏。最好在迁移过程中走很长的路并根据需要手动设置日期,并且绝对在应该跟踪活动的时间戳字段上设置默认静态日期。
    【解决方案2】:

    我使用以下迁移将时间戳添加到现有表并用当前时间填充它们:

    defmodule MyApp.AddTimestampsToChannels do
      use Ecto.Migration
    
      def up do
        alter table(:channels) do
          timestamps null: true
        end
    
        execute """
        UPDATE channels
        SET updated_at=NOW(), inserted_at=NOW()
        """
    
        alter table(:channels) do
          modify :inserted_at, :utc_datetime, null: false
          modify :updated_at, :utc_datetime, null: false
        end
      end
    
      def down do
        alter table(:channels) do
          remove :inserted_at
          remove :updated_at
        end
      end
    end
    

    还有其他方法可以做到这一点。例如,如果您有一些相关的表,您可以从中借用初始时间戳:

    execute """
    UPDATE channels
    SET inserted_at=u.inserted_at,
        updated_at=u.updated_at
    FROM
      (SELECT id,
              inserted_at,
              updated_at
       FROM accounts) AS u
    WHERE u.id=channels.user_id;
    """
    

    【讨论】:

      【解决方案3】:

      我猜您在尝试更新记录时会遇到这种情况,我可以想到 2 种可能的解决方案,您可以通过运行 UPDATE 查询来触摸表中的inserted_at 列,或者像这样向您的 ecto 模型添加一个函数

        def create_changeset(model, attrs) do
          model
          |> cast(attrs, @required_fields, @optional_fields)
          |> update_inserted_at
        end
      
        defp update_inserted_at(changeset) do
          # check if the updated field is null set a new date
        end
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-03-18
        • 1970-01-01
        • 2011-11-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-03-31
        • 1970-01-01
        相关资源
        最近更新 更多