Insert_all не соответствует типу: 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

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
0
1 993
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

У вас есть строка для поля 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 - преобразование строки ISO в примитивный тип utc_datetime


Обратите внимание, что если вы используете наборы изменений 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 / репо / миграции / 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 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 ($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. Вторая вставка вернула структуру набора изменений, а не структуру Edtr, как первая вставка.

  2. Вторая insert () вернула error: ..., а не ok: ..., а набор изменений содержит:

    ошибки: [user_id: {"не может быть пустым", [проверка:: требуется]}],

Затем, если вы перечислите все в таблице базы данных:

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)>

... вы можете видеть, что ревизия с ошибкой ничего не вставила в базу данных, потому что там всего три записи.

@Priz, В моем ответе тебе есть на что посмотреть.

7stud 11.12.2018 10:39

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)
|> ...

Другие вопросы по теме