elixir - insert_all 不匹配类型 :utc_datetime

标签 elixir phoenix-framework ecto

所以我有一个对象数组,我想使用insert_all 保存它。我收到一条错误消息,指出我的某个字段不匹配。

edtr.json

{
"edtrs":
[
    {
        "dtr_date": "2018-12-29T16:00:00.000Z",
        "user_id": "8189f04b-e3d7-4d17-8df2-fedbeb0399b1"
    },
    {
        "dtr_date": "2018-12-30T16:00:00.000Z",
        "user_id": "8189f04b-e3d7-4d17-8df2-fedbeb0399b1"
    }
]}

post_controller

 def create_edtr(edtrs) do
maps =
  Enum.map(edtrs["edtrs"], fn(item) ->
    %{dtr_date: item["dtr_date"], user_id: item["user_id"]}
  end)

IO.inspect maps, label: "MAPS"
Repo.insert_all(Edtr,maps) end

错误

HrisApp.Attendance.Edtr.dtr_date in insert_all does not match type :utc_datetime

最佳答案

您的 dtr_date 字段有一个字符串,但是在数据库表的架构中,您必须指定 dtr_date 字段是 :utc_datetime 类型,因此 ecto 要求您将 DateTime 结构传递给它。

iex(1)> date_string = "2018-12-30T16:00:00.000Z"             
"2018-12-30T16:00:00.000Z"

iex(2)> {:ok, dt_struct, utc_offset} = DateTime.from_iso8601(date_string)
{:ok, #DateTime<2018-12-30 16:00:00.000Z>, 0}

iex(3)> dt_struct
#DateTime<2018-12-30 16:00:00.000Z>

看这里:

Phoenix/Ecto - converting ISO string into utc_datetime primitive type


请注意,如果您使用 ecto 变更集,则 cast() 会将数据转换为适合您的类型。这是一个例子:

目录结构:

lib/
    myapp/
         edtr_api/
                 edtr.ex
                 edtr_api.ex
    myapp_web/
 

edtr.ex:

defmodule Myapp.EdtrApi.Edtr do

  use Ecto.Schema
  import Ecto.Changeset
  alias Myapp.EdtrApi.Edtr

  schema "edtrs" do
    field :dtr_date, :utc_datetime, null: false
    field :user_id, :string

    timestamps()
  end

  def create_changesets(edtr_data) do
    Enum.map(edtr_data, fn data ->
      %Edtr{}
      |> cast(data, [:dtr_date, :user_id])
      |> validate_required([:dtr_date, :user_id])
      |> validate_length(:user_id, min: 10, max: 100)
    end)
  end

end

edtr_api.ex:

defmodule Myapp.EdtrApi do
  alias Myapp.EdtrApi.Edtr
  alias Myapp.Repo
  
  def get_data() do 
    [
      %{
          "dtr_date" => "2018-12-29T16:00:00.000Z",
          "user_id" => "8189f04b-e3d7-4d17-8df2-fedbeb0399b1"
      },
      %{
          "dtr_date" => "2018-12-30T16:00:00.000Z",
      },
    ]
  end

  def insert_edtrs() do
    changesets = Edtr.create_changesets(get_data())
    Enum.map(changesets, fn changeset -> Repo.insert(changeset) end) 
  end

  def all() do
    Repo.all(Edtr)
  end

end

然后你需要创建一个迁移文件:

~/phoenix_apps/myapp$ mix ecto.gen.migration create_edtrs

然后更改迁移文件以模仿您的 Edtr 架构:

myapp/priv/repo/migrations/20181211070748_create_edtrs.exs:

defmodule Myapp.Repo.Migrations.CreateEdtrs do
  use Ecto.Migration

  def change do
    create table(:edtrs) do
      add :dtr_date, :utc_datetime, null: false
      add :user_id, :string

      timestamps()
    end

  end
end

然后执行迁移以在数据库中创建表:

 ~/phoenix_apps/myapp$ mix ecto.migrate

现在在 iex 中尝试一下:

$ iex -S mix
Erlang/OTP 20 [erts-9.3] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false]
Interactive Elixir (1.6.6) - press Ctrl+C to exit (type h() ENTER for help)

iex(1)> alias Myapp.EdtrApi        
Myapp.EdtrApi

iex(2)> alias Myapp.EdtrApi.Edtr   
Myapp.EdtrApi.Edtr

iex(3)> Myapp.Repo.delete_all(Edtr)
[debug] QUERY OK source="edtrs" db=1.0ms decode=1.7ms queue=0.9ms
DELETE FROM "edtrs" AS e0 []
{0, nil}

iex(4)> EdtrApi.insert_edtrs()                  
[debug] QUERY OK db=7.4ms queue=2.1ms
INSERT INTO "edtrs" ("dtr_date","user_id","inserted_at","updated_at") VALUES ($1,$2,$3,$4) RETURNING "id" [#DateTime<2018-12-29 16:00:00Z>, "8189f04b-e3d7-4d17-8df2-fedbeb0399b1", ~N[2018-12-11 09:11:18], ~N[2018-12-11 09:11:18]]
[debug] QUERY OK db=2.3ms queue=1.0ms
INSERT INTO "edtrs" ("dtr_date","user_id","inserted_at","updated_at") VALUES ($1,$2,$3,$4) RETURNING "id" [#DateTime<2018-12-30 16:00:00Z>, "8189f04b-e3d7-4d17-8df2-fedbeb0399b1", ~N[2018-12-11 09:11:18], ~N[2018-12-11 09:11:18]]
[
  ok: %Myapp.EdtrApi.Edtr{
    __meta__: #Ecto.Schema.Metadata<:loaded, "edtrs">,
    dtr_date: #DateTime<2018-12-29 16:00:00Z>,
    id: 1,
    inserted_at: ~N[2018-12-11 09:11:18],
    updated_at: ~N[2018-12-11 09:11:18],
    user_id: "8189f04b-e3d7-4d17-8df2-fedbeb0399b1"
  },
  ok: %Myapp.EdtrApi.Edtr{
    __meta__: #Ecto.Schema.Metadata<:loaded, "edtrs">,
    dtr_date: #DateTime<2018-12-30 16:00:00Z>,
    id: 2,
    inserted_at: ~N[2018-12-11 09:11:18],
    updated_at: ~N[2018-12-11 09:11:18],
    user_id: "8189f04b-e3d7-4d17-8df2-fedbeb0399b1"
  }
]

iex(5)> 

在输出中,您可以看到在返回的 Edtr 结构 中,日期字符串已转换为 DateTime 结构。调用 cast() 就是这样做的。

好的,现在让我们从数据中删除一个 user_id:

  def get_edtrs() do 
    [
      %{
          "dtr_date" => "2018-12-29T16:00:00.000Z",
          "user_id" => "8189f04b-e3d7-4d17-8df2-fedbeb0399b1"
      },
      %{
          "dtr_date" => "2018-12-30T16:00:00.000Z",
      },
    ]
  end

再试一次:

~/phoenix_apps/myapp$ iex -S mix
Erlang/OTP 20 [erts-9.3] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false]
Compiling 1 file (.ex)
Interactive Elixir (1.6.6) - press Ctrl+C to exit (type h() ENTER for help)

iex(1)> alias Myapp.EdtrApi.Edtr   
Myapp.EdtrApi.Edtr

iex(2)> EdtrApi.insert_edtrs()     
[debug] QUERY OK db=4.4ms decode=2.9ms queue=1.5ms
INSERT INTO "edtrs" ("dtr_date","user_id","inserted_at","updated_at") VALUES ($1,$2,$3,$4) RETURNING "id" [#DateTime<2018-12-29 16:00:00Z>, "8189f04b-e3d7-4d17-8df2-fedbeb0399b1", ~N[2018-12-11 09:12:17], ~N[2018-12-11 09:12:17]]
[
  ok: %Myapp.EdtrApi.Edtr{
    __meta__: #Ecto.Schema.Metadata<:loaded, "edtrs">,
    dtr_date: #DateTime<2018-12-29 16:00:00Z>,
    id: 3,
    inserted_at: ~N[2018-12-11 09:12:17],
    updated_at: ~N[2018-12-11 09:12:17],
    user_id: "8189f04b-e3d7-4d17-8df2-fedbeb0399b1"
  },
  error: #Ecto.Changeset<
    action: :insert,
    changes: %{dtr_date: #DateTime<2018-12-30 16:00:00Z>},
    errors: [user_id: {"can't be blank", [validation: :required]}],
    data: #Myapp.EdtrApi.Edtr<>,
    valid?: false
  >
]

iex(3)> 

如果您检查输出的最后一部分,请注意:

  1. 第二个插入返回一个 Changeset 结构,而不是像第一个插入那样的 Edtr 结构。

  2. 第二个 insert() 返回了 error: ... 而不是 ok: ...,并且 Changeset 包含:

    错误:[user_id: {"can't be blank", [validation: :required]}],

然后,如果您列出数据库表中的所有内容:

iex(4)> EdtrApi.all             
[debug] QUERY OK source="edtrs" db=2.8ms queue=2.0ms
SELECT e0."id", e0."dtr_date", e0."user_id", e0."inserted_at", e0."updated_at" FROM "edtrs" AS e0 []
[
  %Myapp.EdtrApi.Edtr{
    __meta__: #Ecto.Schema.Metadata<:loaded, "edtrs">,
    dtr_date: #DateTime<2018-12-29 16:00:00Z>,
    id: 1,
    inserted_at: ~N[2018-12-11 09:11:18],
    updated_at: ~N[2018-12-11 09:11:18],
    user_id: "8189f04b-e3d7-4d17-8df2-fedbeb0399b1"
  },
  %Myapp.EdtrApi.Edtr{
    __meta__: #Ecto.Schema.Metadata<:loaded, "edtrs">,
    dtr_date: #DateTime<2018-12-30 16:00:00Z>,
    id: 2,
    inserted_at: ~N[2018-12-11 09:11:18],
    updated_at: ~N[2018-12-11 09:11:18],
    user_id: "8189f04b-e3d7-4d17-8df2-fedbeb0399b1"
  },
  %Myapp.EdtrApi.Edtr{
    __meta__: #Ecto.Schema.Metadata<:loaded, "edtrs">,
    dtr_date: #DateTime<2018-12-29 16:00:00Z>,
    id: 3,
    inserted_at: ~N[2018-12-11 09:12:17],
    updated_at: ~N[2018-12-11 09:12:17],
    user_id: "8189f04b-e3d7-4d17-8df2-fedbeb0399b1"
  }
]

iex(5)>

...您可以看到有错误的变更集没有在数据库中插入任何内容,因为只有三个条目。

关于elixir - insert_all 不匹配类型 :utc_datetime,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53717074/

相关文章:

elixir - Phoenix Path Helper 和 Phoenix.Param 用于基于日期的 URI

postgresql - Elixir/Phoenix 无法连接到云托管的 Postgres 数据库

elixir - 如何在 Ecto 中更新一对多关联

elixir - 在 phoenix LiveView 中使用外部库

elixir - Phoenix - 将 Ecto 查询结果返回给特定的客户端

postgresql - 到我的docker postgres数据库容器的连接数开始于11?

function - 如何在 Elixir 中动态调用运算符

elixir - 使用模块作为常量

Elixir Ecto : Multiple belongs_to relationship in a schema

elixir - Ecto - 无法删除自定义命名的唯一索引