设置Phoenix Framework和Ecto以使用UUID:如何插入生成的值?

The*_*uts 21 postgresql elixir ecto phoenix-framework

几天前,我开始使用Elixir和Phoenix Framework(v 0.12.0)和Postgres数据库.我正在尝试创建一个具有UUID主键的表,我更喜欢顺序默认值.

在使用mix phoenix.gen.html生成模型和迁移文件并遵循Phoenix文档中的其他步骤之后,我已经更改了

def model do
    quote do
      use Ecto.Model
    end
  end
Run Code Online (Sandbox Code Playgroud)

web.ex

def model do
  quote do
    use Ecto.Model
    @primary_key {:id, :uuid, []}
    @foreign_key_type :uuid
  end
end
Run Code Online (Sandbox Code Playgroud)

正如Ecto文档中提到的那样.我也将迁移更改为

create table(:tblname, primary_key: false) do
  add :id, :uuid, primary_key: true
  [other columns]
end
Run Code Online (Sandbox Code Playgroud)

不幸的是,当我尝试从自动生成的表单向表中添加一个条目时,我收到一个错误,因为它id是null.如果我手动将id-column 添加到模型中,则会收到列已存在的错误.如果我忽略设置primary_key为false table/2并删除id列,则使用顺序id列生成表.

我是否需要id在变更集中手动设置,或者在设置我的应用程序以使用UUID时出错?提前致谢

Jos*_*lim 42

编辑:我已将此答案更新为Ecto v2.0.您可以在最后阅读上一个答案.

Ecto v2

自最初答案以来,处理Ecto中的UUID变得更加直截了当.Ecto有两种类型的ID::id:binary_id.第一个是我们从数据库中知道的整数ID,第二个是数据库特定的二进制.对于Postgres,它是一个UUID.

要将UUID作为主键,请先在迁移中指定它们:

create table(:posts, primary_key: false) do
  add :id, :binary_id, primary_key: true
end
Run Code Online (Sandbox Code Playgroud)

然后在您的模型模块中(schema块外):

@primary_key {:id, :binary_id, autogenerate: true}
Run Code Online (Sandbox Code Playgroud)

当您指定:autogenerate选项时:binary_id,Ecto将保证适配器或数据库将为您生成它.但是,如果您愿意,仍然可以手动生成它.顺便说一句,您可以:uuid在迁移和Ecto.UUID架构中使用,而不是:binary_id,它的好处:binary_id是它可以跨数据库移植.

Ecto v1

您需要告诉数据库如何自动为您生成UUID.或者您需要从应用程序端生成一个.这取决于你喜欢哪一个.

在我们继续之前,重要的是要说你正在使用:uuid它将返回二进制文件而不是人类可读的UUID.你很可能想要使用Ecto.UUID它将其格式化为一个字符串(aaaa-bbb-ccc -...),这就是我将在下面使用的内容.

在数据库中生成

在迁移中,为字段定义默认值:

add :id, :uuid, primary_key: true, default: fragment("uuid_generate_v4()")
Run Code Online (Sandbox Code Playgroud)

我假设你在PostgreSQL上运行.您需要CREATE EXTENSION "uuid-ossp"在pgAdmin中安装uuid-ossp扩展或添加execute "CREATE EXTENSION \"uuid-ossp\""迁移.有关UUID生成器的更多信息,请访问此处.

回到Ecto,在你的模型中,要求Ecto在插入/更新后从数据库中读取字段:

@primary_key {:id, Ecto.UUID, read_after_writes: true}
Run Code Online (Sandbox Code Playgroud)

现在,当您插入时,数据库将生成一个默认值,Ecto将读回它.

在应用程序中生成

您需要定义一个为您插入UUID的模块:

defmodule MyApp.UUID do
  def put_uuid(changeset) do
    Ecto.Changeset.put_change(changeset, :id, Ecto.UUID.generate())
  end
end
Run Code Online (Sandbox Code Playgroud)

并将其用作回调:

def model do
  quote do
    use Ecto.Model
    @primary_key {:id, Ecto.UUID, []}
    @foreign_key_type Ecto.UUID
    before_insert MyApp.UUID, :put_uuid, []
  end
end
Run Code Online (Sandbox Code Playgroud)

before_insert 是一个回调函数,它将使用给定的参数调用给定函数的给定模块,其中一个变量集表示作为第一个参数给出的插入内容.

这应该是全部.顺便说一下,未来有可能会更精简.:)

  • 另一个重要的事情是将引用类型指定为uuid.`add:post_id,references(:posts,type :: uuid)` (9认同)

cod*_*hal 5

同样,在创建新的项目传递选项时--binary-id,使用UUID作为默认主键。(启动Ecto v2

mix phx.new project_name --binary-id
Run Code Online (Sandbox Code Playgroud)