【问题标题】:Ecto will not insert a :date field into the database - why?Ecto 不会在数据库中插入 :date 字段 - 为什么?
【发布时间】:2019-11-28 21:45:21
【问题描述】:

我在学习 Elixir 和 Ecto 时遇到了一个问题。这个想法是建立一个标准的帖子/cmets 页面来了解基础知识是如何工作的。我现在已经定义了模式,写入了迁移并在尝试通过 Repo 将数据插入数据库 (PostgreSQL) 时遇到错误。我已经进行了大量的网络搜索和文档阅读,这让我相信这是一个应该可以工作的场景,但我在某个地方犯了一个愚蠢的错误,我只是看不到。

它们的定义如下:

lib/hello/schemas.ex

defmodule Hello.PostAuthor do
    use Ecto.Schema

    schema "post_authors" do
        field :name, :string
    end
end

defmodule Hello.CommentAuthor do
    use Ecto.Schema

    schema "comment_authors" do
        field :name, :string
    end
end

defmodule Hello.Comment do
    use Ecto.Schema

    schema "comments" do
        has_one :author, Hello.CommentAuthor
        field :date, :date
        field :body, :string
    end
end

defmodule Hello.Post do
    use Ecto.Schema

    schema "posts" do
        has_one :author, Hello.PostAuthor
        field :date, :date
        field :body, :string
        has_many :comments, Hello.Comment
    end
end

如您所见,我有两个带有 :date 类型的字段 - 在帖子和评论模式上。对应的迁移如下:

defmodule Hello.Repo.Migrations.CreatePosts do
  use Ecto.Migration

  def change do
    create table(:post_authors) do
      add :name, :string
    end

    create table(:comment_authors) do
      add :name, :string
    end

    create table(:comments) do
      add :author, references(:comment_authors)
      add :date, :date
      add :body, :string
    end

    create table(:posts) do
      add :author, references(:post_authors), null: false
      add :date, :date
      add :body, :string
      add :comments, references(:comments)

      timestamps()
    end
  end
end

现在,当我启动 iex -S mix 时,我可以成功创建所有结构:

iex(1)> post_author = %Hello.PostAuthor{name: "John"} 
%Hello.PostAuthor{
  __meta__: #Ecto.Schema.Metadata<:built, "post_authors">,
  id: nil,
  name: "John"
}

iex(2)> comment_author = %Hello.PostAuthor{name: "Adam"}
%Hello.PostAuthor{
  __meta__: #Ecto.Schema.Metadata<:built, "post_authors">,
  id: nil,
  name: "Adam"
}


iex(3)> comment = %Hello.Comment{author: comment_author, date: ~D[2019-01-01], body: "this is a comment"}
%Hello.Comment{
  __meta__: #Ecto.Schema.Metadata<:built, "comments">,
  author: %Hello.PostAuthor{
    __meta__: #Ecto.Schema.Metadata<:built, "post_authors">,
    id: nil,
    name: "Adam"
  },
  body: "this is a comment",
  date: ~D[2019-01-01],
  id: nil
}

iex(4)> post = %Hello.Post{author: post_author, date: ~D[2019-01-01], body: "this is a post", comments: [comment]}
%Hello.Post{
  __meta__: #Ecto.Schema.Metadata<:built, "posts">,
  author: %Hello.PostAuthor{
    __meta__: #Ecto.Schema.Metadata<:built, "post_authors">,
    id: nil,
    name: "John"
  },
  body: "this is a post",
  comments: [%Hello.Comment{
    __meta__: #Ecto.Schema.Metadata<:built, "comments">,
    author: %Hello.PostAuthor{
      __meta__: #Ecto.Schema.Metadata<:built, "post_authors">,
      id: nil,
      name: "Adam"
    },
    body: "this is a comment",
    date: ~D[2019-01-01],
    id: nil
  }],
  date: ~D[2019-01-01],
  id: nil
}

当我调用Hello.Repo.insert(post)(其中post 是代表Hello.Post 架构的结构)时,问题就出现了。我收到了类似序列化错误:

iex(8)> Hello.Repo.insert(post)                                                                     [debug] QUERY OK db=0.1ms
begin []
[debug] QUERY ERROR db=1.6ms
INSERT INTO "posts" ("body","date") VALUES ($1,$2) RETURNING "id" ["this is a post", ~D[2019-01-01]]
[debug] QUERY OK db=0.1ms
rollback []
** (DBConnection.EncodeError) Postgrex expected a binary, got ~D[2019-01-01]. Please make sure the value you are passing matches the definition in your table or in your query or convert the value accordingly.
    (postgrex) lib/postgrex/type_module.ex:897: Postgrex.DefaultTypes.encode_params/3
    (postgrex) lib/postgrex/query.ex:75: DBConnection.Query.Postgrex.Query.encode/3
    (db_connection) lib/db_connection.ex:1148: DBConnection.encode/5
    (db_connection) lib/db_connection.ex:1246: DBConnection.run_prepare_execute/5
    (db_connection) lib/db_connection.ex:540: DBConnection.parsed_prepare_execute/5
    (db_connection) lib/db_connection.ex:533: DBConnection.prepare_execute/4
    (postgrex) lib/postgrex.ex:198: Postgrex.query/4
    (ecto_sql) lib/ecto/adapters/sql.ex:666: Ecto.Adapters.SQL.struct/10
    (ecto) lib/ecto/repo/schema.ex:651: Ecto.Repo.Schema.apply/4
    (ecto) lib/ecto/repo/schema.ex:262: anonymous fn/15 in Ecto.Repo.Schema.do_insert/4
    (ecto) lib/ecto/repo/schema.ex:916: anonymous fn/3 in Ecto.Repo.Schema.wrap_in_transaction/6
    (ecto_sql) lib/ecto/adapters/sql.ex:898: anonymous fn/3 in Ecto.Adapters.SQL.checkout_or_transaction/4
    (db_connection) lib/db_connection.ex:1415: DBConnection.run_transaction/4

这就是我迷路的地方。架构和迁移都需要 :date 。我相信~D[2019-01-01] 是一个日期。 PostgreSQL 将日期定义为一个 4 字节的二进制值。我期待 Ecto.Adapters.Postgres 将 elixir 日期结构转换为 Postgres 二进制值。这没有发生。为什么?

【问题讨论】:

    标签: postgresql elixir ecto


    【解决方案1】:

    结构本身只是原始数据。您应该按照文档中所示通过Ecto.Changeset,特别是要使用Ecto.Changeset.cast/4 将所有类型转换为相应的DB 类型。

    转换将自动完成,但您需要显式调用cast/4(因此是Changeset,)否则适配器不知道如何转换您的ecto类型。

    【讨论】:

    • 非常有意义。我原以为错过了一些基本的东西。谢谢你帮助我。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-14
    • 1970-01-01
    • 2016-01-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多