insert_all 不匹配类型:utc_datetime
insert_all does not match type :utc_datetime
所以我有一个 对象数组 ,我想使用 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
字段有一个字符串,但在数据库的架构中 table 您必须指定 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>
看这里:
请注意,如果您使用 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
然后执行迁移以在数据库中创建 table:
~/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 (,,,) 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 (,,,) 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 structs
中,日期字符串已转换为 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 (,,,) 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)>
如果您检查输出的最后一部分,请注意:
第二个插入返回了一个 Changeset 结构,而不是像第一个插入那样的 Edtr 结构。
第二个 insert() 返回 error: ...
而不是 ok: ...
,变更集包含:
错误:[user_id:{“不能为空”,[验证::必需]}],
然后,如果您列出数据库中的所有内容 table:
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)>
...您可以看到错误的变更集没有在数据库中插入任何内容,因为只有三个条目。
FWIW,我会把它放在这里。几天前,我 运行 遇到了类似的问题,但原因不同。这个答案可能与所提出的确切问题无关,但我一直很难尝试在互联网上搜索答案,所以就放在这里吧。
至少对于 MySQL
和 MariaEx
驱动程序,此错误可能是由 非零 微秒 引起的在 DateTime
结构中。 AFAIU,前者不允许。是否要将 DateTime
的实例存储到 MySQL
中(例如 DateTime.utc_now()
,)它不会开箱即用,显示消息如上。
要解决此问题,请使用 DateTime.truncate/2
并将 :seconds
作为第二个参数:
now = DateTime.truncate(DateTime.utc_now(), :second)
changeset
|> Ecto.Changeset.put_change(:created_at, now)
|> ...
所以我有一个 对象数组 ,我想使用 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
ininsert_all
does not match type:utc_datetime
您的 dtr_date
字段有一个字符串,但在数据库的架构中 table 您必须指定 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>
看这里:
请注意,如果您使用 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
然后执行迁移以在数据库中创建 table:
~/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 (,,,) 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 (,,,) 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 structs
中,日期字符串已转换为 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 (,,,) 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)>
如果您检查输出的最后一部分,请注意:
第二个插入返回了一个 Changeset 结构,而不是像第一个插入那样的 Edtr 结构。
第二个 insert() 返回
error: ...
而不是ok: ...
,变更集包含:错误:[user_id:{“不能为空”,[验证::必需]}],
然后,如果您列出数据库中的所有内容 table:
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)>
...您可以看到错误的变更集没有在数据库中插入任何内容,因为只有三个条目。
FWIW,我会把它放在这里。几天前,我 运行 遇到了类似的问题,但原因不同。这个答案可能与所提出的确切问题无关,但我一直很难尝试在互联网上搜索答案,所以就放在这里吧。
至少对于 MySQL
和 MariaEx
驱动程序,此错误可能是由 非零 微秒 引起的在 DateTime
结构中。 AFAIU,前者不允许。是否要将 DateTime
的实例存储到 MySQL
中(例如 DateTime.utc_now()
,)它不会开箱即用,显示消息如上。
要解决此问题,请使用 DateTime.truncate/2
并将 :seconds
作为第二个参数:
now = DateTime.truncate(DateTime.utc_now(), :second)
changeset
|> Ecto.Changeset.put_change(:created_at, now)
|> ...