Ecto不会在数据库中插入:date字段-为什么?

问题描述 投票:0回答:1

[学习Elixir和Ecto时遇到问题。这个想法是建立一个标准的帖子/评论页面,以了解基本原理。我已经定义了架构,编写了迁移文件,并尝试通过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个回答
1
投票

结构本身只是原始数据。您应该按照文档中的说明进行Ecto.Changeset操作,特别是要使用Ecto.Changeset将所有类型强制转换为相应的DB类型。

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

© www.soinside.com 2019 - 2024. All rights reserved.