Postgresql 设置Phoenix框架和Exto以使用UUID:如何插入生成的值?
几天前,我开始在Postgres数据库中使用Elixir和Phoenix框架(V0.12.0)。我正在尝试创建一个具有UUID主键的表,我更喜欢它,而不是顺序默认值 在使用Postgresql 设置Phoenix框架和Exto以使用UUID:如何插入生成的值?,postgresql,elixir,phoenix-framework,ecto,Postgresql,Elixir,Phoenix Framework,Ecto,几天前,我开始在Postgres数据库中使用Elixir和Phoenix框架(V0.12.0)。我正在尝试创建一个具有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.
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
正如在外太空文件中提到的。我还将迁移更改为
create table(:tblname, primary_key: false) do
add :id, :uuid, primary_key: true
[other columns]
end
不幸的是,当我试图从自动生成的表单向表中添加条目时,我得到了一个错误,因为id
为空。如果我手动将id
-列添加到模型中,我会收到一个错误,即该列已经存在。如果我在table/2
中忽略将primary\u键设置为false,并删除id
列,则该表将生成一个顺序id
-列
我需要在变更集中手动设置id
,还是在设置我的应用程序以使用UUIDs时出错?提前感谢编辑:我已将此答案更新为Ecto v2.0。你可以在最后阅读前面的答案
外星2型
从最初的答案开始,在EXTO中处理UUID变得更加直截了当。Ecto有两种类型的id::id
和:binary\u id
。第一个是我们从数据库中知道的整数ID,第二个是特定于数据库的二进制文件。对于博士后来说,这是一个UUID
要将UUID作为主键,请首先在迁移中指定它们:
create table(:posts, primary_key: false) do
add :id, :binary_id, primary_key: true
end
然后在模型模块中(在模式
块之外):
当您为:binary\u id
指定:autogenerate
选项时,EXTO将保证适配器或数据库将为您生成它。但是,如果愿意,您仍然可以手动生成它。顺便说一句,您可以在迁移中使用:uuid
,在模式中使用exto.uuid
,而不是:binary\u id
,:binary\u id
的好处是它可以跨数据库移植
外胚层v1
您需要告诉数据库如何自动生成UUID。或者您需要从应用程序端生成一个。这取决于你喜欢哪一个
在我们继续之前,有一点很重要,那就是您使用的是:uuid
,它将返回二进制文件,而不是人类可读的uuid。很可能您想要使用exto.UUID
,它将把它格式化为字符串(aaaa-bbb-ccc-…),这就是我将在下面使用的内容
在数据库中生成
在迁移中,为字段定义默认值:
add :id, :uuid, primary_key: true, default: fragment("uuid_generate_v4()")
我假设您正在运行PostgreSQL。您需要使用pgAdmin中的创建扩展“uuid ossp”
安装uuid ossp扩展,或者在迁移中添加执行“创建扩展\”uuid ossp \
。有关的更多信息
回到EXTO,在您的模型中,要求EXTO在插入/更新后从数据库中读取字段:
@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\u insert
是一个回调函数,它将使用给定的参数调用给定函数中的给定模块,其中一个变更集表示要插入的内容作为第一个参数
就这些了。顺便说一句,将来这项工作可能会更加精简。:) 同样在创建新的项目过程选项时--binary-id
使用UUID作为默认主键。(启动exto v2)
谢谢你的快速回复!不幸的是,这两种方法都会导致Postgrex出错。如果我在我的应用程序中生成UUID,我会收到以下错误:Postgrex.Extensions.Binary.encode/4中没有匹配的函数子句。如果我将exto.Changeset.put_new_change/3链接到模型中的管道以将UUID添加到参数中,我会收到相同的错误。如果我让Postgres通过调用UUID_generate_v4()来生成UUID,Postgrex会抱怨函数不存在:(Postgrex.Error)Error(undefined_函数):在pgAdmin
UUID_generate_v4()中执行CREATE EXTENSION“UUID ossp”
后,函数UUID_generate_v4()不存在,该行已正确创建。但是,索引视图返回UnicodeConversionError。我不确定Postgres为什么抱怨uuid\u generate\u v4()
。也许你需要安装一些模块?我不记得确切的步骤,所以我添加了一个指向文档的链接。其他错误是因为您在模型中使用了:uuid
,而不是exto.uuid
,我已经在第二段的后面部分解释了为什么要使用它。我还更新了代码示例,这样其他人就不会犯与我们相同的错误!感谢您的反馈。另一个重要的事情是将引用类型指定为uuid<代码>添加:post\u id,引用(:posts,type::uuid)
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
mix phx.new project_name --binary-id