也许我做错了什么,但我只是像这样创建了类型和字段:
# creating the database type
execute("create type post_status as enum ('published', 'editing')")
# creating a table with the column
create table(:posts) do
add :post_status, :post_status, null: false
end
然后只需将字段变为字符串:
field :post_status, :string
看起来它能够工作。
为 @JustMichael 进行小改进。如果需要回滚,您可以使用:
def down do
drop table(:posts)
execute("drop type post_type")
end
总结所有答案和评论中的零散信息。有关使用的SQL命令,请参阅PostgreSQL手册中的"枚举类型"。
Ecto
3.0.0及以上版本自从Ecto
3.0.0以来,有Ecto.Migration.execute/2
,它“执行可逆的SQL命令”,因此可以在change/0
中使用:
使用mix ecto.gen.migration create_orders
生成迁移后:
defmodule CreateOrders do
use Ecto.Migration
@type_name :order_status
def change do
execute(
"""
CREATE TYPE #{@type_name}
AS ENUM ('placed','shipping','delivered')
""",
"DROP TYPE #{@type_name}"
)
create table(:orders) do
add :order_status, @type_name, null: false
timestamps()
end
end
end
与"Ecto 2.x.x及以下版本"中相同。
Ecto
2.x.x及以下版本使用mix ecto.gen.migration create_orders
生成迁移后:
defmodule CreateOrders do
use Ecto.Migration
@type_name :order_status
def up do
execute(
"""
CREATE TYPE #{@type_name}
AS ENUM ('placed','shipping','delivered'})
""")
create table(:orders) do
add :order_status, @type_name, null: false
timestamps()
end
end
def down do
drop table(:orders)
execute("DROP TYPE #{@type_name}")
end
end
由于模式无法看到迁移中创建的数据库类型,因此在Order.changeset/2
中使用Ecto.Changeset.validate_inclusion/4
来确保输入有效。
defmodule Order do
use Ecto.Schema
import Ecto.Changeset
schema "orders" do
field :order_status, :string
timestamps()
end
def changeset(
%__MODULE__{} = order,
%{} = attrs
) do
fields = [ :order_status ]
order
|> cast(attrs, fields)
|> validate_required(fields)
|> validate_inclusion(
:order_status,
~w(placed shipping delivered)
)
end
end
order
模式上的statuses
函数会发生变化(例如更改名称/删除函数),这将防止应用程序编译。在我看来,最好将实时代码排除在迁移之外,并将其视为始终可以编译和运行的常青文档。如果添加了新状态,请创建一个新的迁移以记录它(您永远不会运行旧的迁移以升级类型)。 - Dorian您需要为每个postgresql枚举创建一个Ecto类型。在模式定义中,您只需将类型设置为:string
。在迁移中,您将类型设置为模块名称。然而,这可能会变得非常繁琐,因此我在我的项目中使用以下宏来使用Postgresql枚举:
defmodule MyDB.Enum do
alias Postgrex.TypeInfo
defmacro defenum(module, name, values, opts \\ []) do
quote location: :keep do
defmodule unquote(module) do
@behaviour Postgrex.Extension
@typename unquote(name)
@values unquote(values)
def type, do: :string
def init(_params, opts), do: opts
def matching(_), do: [type: @typename]
def format(_), do: :text
def encode(%TypeInfo{type: @typename}=typeinfo, str, args, opts) when is_atom(str), do: encode(typeinfo, to_string(str), args, opts)
def encode(%TypeInfo{type: @typename}, str, _, _) when str in @values, do: to_string(str)
def decode(%TypeInfo{type: @typename}, str, _, _), do: str
def __values__(), do: @values
defoverridable init: 2, matching: 1, format: 1, encode: 4, decode: 4
unquote(Keyword.get(opts, :do, []))
end
end
end
end
可能的用途:
import MyDB.Enum
defenum ColorsEnum, "colors_enum", ~w"blue red yellow"
ColorsEnum
将作为模块名称,"colors_enum"
将是 Postgresql 内部的枚举名称:您需要在数据库迁移中添加一个语句来创建枚举类型。最后一个参数是枚举值列表。我使用了 ~w
符号,它将字符串按空格拆分,以显示其简洁性。当原子值通过 Ecto schema 时,我还添加了一个子句将其转换为字符串值。
:string
? - Jean-Pierre Bécotte:string
。Enum类型之所以对于postgrex
发挥作用很必要,是因为它将内部postgresql oid
映射到Elixir类型,而在这种情况下是一个字符串。 - asongeEcto_enum现在支持Postgres的枚举类型。https://github.com/gjaldon/ecto_enum#using-postgress-enum-type
ecto_enum
不再维护。 - ryanwinchester除了 @JustMichael 和 @swennemen 所说的之外,截至ecto 2.2.6版本,我们有Ecto.Migration.execute/2函数,它需要up和down参数。所以我们可以在我们的迁移文件中的change
块内执行以下代码:
execute("create type post_status as enum ('published', 'editing')", "drop type post_status")
这样,ecto就能有效地回滚。
change do
块内。第二个块位于模型文件中,在schema do
块内。 - jeffreymatthiasvalidate_inclusion(changeset, :post_status, ["published", "editing"])
。 - sinned