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

hfyxw5xn  于 2022-12-09  发布在  PostgreSQL
关注(0)|答案(3)|浏览(120)

几天前,我开始使用Elixir和Phoenix Framework(v 0.12.0)与Postgres数据库。我试图创建一个具有UUID主键的表,我更喜欢顺序默认值。
在使用mix phoenix.gen.html生成模型和迁移文件并遵循Phoenix文档中的其他步骤之后,我已经更改了

def model do
    quote do
      use Ecto.Model
    end
  end

字符串
web.ex中,

def model do
  quote do
    use Ecto.Model
    @primary_key {:id, :uuid, []}
    @foreign_key_type :uuid
  end
end


正如在Ecto文档中提到的那样。我还将迁移更改为

create table(:tblname, primary_key: false) do
  add :id, :uuid, primary_key: true
  [other columns]
end


不幸的是,当我试图从自动生成的表单中添加一个条目到表中时,我得到一个错误,因为id是null。如果我手动添加一个id-列到模型中,我会收到一个错误,该列已经存在。如果我忽略了在table/2中将primary_key设置为false并删除id列,该表用顺序的id列生成。
我是否需要在修改器中手动设置id,或者我在设置应用程序以使用UUID时出错了?

amrnrhlw

amrnrhlw1#

编辑:我已经将这个答案更新到了Ecto v2.0。你可以在最后阅读以前的答案。

体外循环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

字符串
然后在模型模块中(schema块外部):

@primary_key {:id, :binary_id, autogenerate: true}


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

体外循环v1

你需要告诉你的数据库如何自动为你生成UUID。或者你需要从应用程序端生成一个。这取决于你喜欢哪一个。
在我们继续之前,有一点很重要,那就是你使用的:uuid将返回二进制文件,而不是人类可读的UUID。很可能你想使用Ecto.UUID,它将把它格式化为字符串(aaaa-bbb-ccc-...),这就是我下面要使用的。

数据库生成

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

add :id, :uuid, primary_key: true, default: fragment("uuid_generate_v4()")


我假设你运行的是PostgreSQL。你需要在pgAdmin中安装uuid-ossp扩展和CREATE EXTENSION "uuid-ossp",或者在迁移中添加execute "CREATE EXTENSION \"uuid-ossp\""。关于the UUID generator can be found here的更多信息。
回到Ecto,在你的模型中,要求Ecto在插入/更新后从数据库中读取字段:

@primary_key {:id, Ecto.UUID, read_after_writes: true}


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

在应用中生成

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

defmodule MyApp.UUID do
  def put_uuid(changeset) do
    Ecto.Changeset.put_change(changeset, :id, Ecto.UUID.generate())
  end
end


并将其用作回调:

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


before_insert是一个回调函数,它将在给定的函数中调用给定的模块,并将一个表示插入内容的变量作为第一个参数。
这应该就是全部了。顺便说一句,将来有可能会更精简。:)

6qfn3psc

6qfn3psc2#

另外,在创建新项目时,通过选项--binary-id使用UUID作为默认主键。(启动Ecto v2

mix phx.new project_name --binary-id

字符串

shyt4zoc

shyt4zoc3#

在创建新项目的时候,你忘记传递--binary-id选项了吗?没问题!
打开config.exs文件
搜索Ecto,你通常会找到这样的东西:

config :myproject,
  ecto_repos: [myproject.Repo]

字符串
只需将--binary-id添加到配置中:

config :myproject,
  ecto_repos: [myproject.Repo]
  generators: [binary_id: true]


当您生成一个新的模式时,它现在将被配置为记住这一点。
就是这样!

相关问题