2016-02-06 27 views
11

枚舉類型在PostgreSQL,我們可以做這樣的事情:如何使用Postgres的具有外生

CREATE TYPE order_status AS ENUM ('placed','shipping','delivered') 

Ecto's official doc,沒有提供原生類型映射Postgres的枚舉類型。這module爲枚舉結構提供了一個自定義類型,但它映射到數據庫中的一個整數。我可以輕鬆地使用該庫,但我更願意使用數據庫附帶的本機枚舉類型。

外生還提供了一個方法來創建custom types,但據我所看到的,自定義類型必須映射到本地外生型...

任何人都知道這是否可以與外生的模式來完成?如果是的話,遷移將如何工作?

回答

5

您需要爲每個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 sigil,它將用空格分隔字符串,以顯示這可能是多麼簡潔。我還添加了一個子句,它在通過Ecto模式時將原子值轉換爲字符串值。

+0

感謝您的回答!這看起來很有希望,但我看不出如何在架構中使用ColorsEnum。 (我得到了遷移部分)。當我在模式中添加字段時,應該使用什麼類型? ':string'? –

+0

是的,你應該在實際的模式定義中使用':string'。爲什麼'postgrex'完成它的事情需要枚舉類型,因爲它將內部postgresql'oid'映射到Elixir類型,在這種情況下,它是一個字符串。 – asonge

+0

@ason​​ge雖然我尊重你的Elixir Fu,但我個人並不需要每個postgresql枚舉的Ecto類型。也許我錯過了,但爲什麼你需要它?只是使用字符串和變更集驗證器https://hexdocs.pm/ecto/Ecto.Changeset.html#validate_inclusion/4 –

22

也許我做錯了什麼事,但我剛剛創建這樣的類型和字段:

# 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 

,它似乎工作。

+10

對於那些剛剛接觸框架的品牌,JustMichael的解決方案很有用,但我想我會添加代碼需要去的地方。第一個代碼段位於遷移文件中,位於''''''''''do'''塊內。 第二個塊進入模型文件的內部,位於''schema_do'''塊內。 – jeffreymatthias

+1

當你傳遞一個未發佈或編輯的字符串時會發生什麼?發生什麼類型的錯誤? –

+1

@TerenceChow DB很可能會引發一些事情,您的數據庫操作將失敗。 – JustMichael

7

@JustMichael的小增強。如果您需要回滾,您可以使用:

def down do 
    drop table(:posts) 
    execute("drop type post_type") 
end 
+0

是的!好點子! –

3

添加到什麼@JustMichael和@swennemen說......作爲外生2.2.6我們有外生.Migration.execute/2需要一個向上和向下arg。因此,我們可以這樣做:

execute("create type post_status as enum ('published', 'editing')", "drop type post_status")

change塊內我們遷移文件,以及外生將能夠有效地回滾。

相關問題