2016-02-06 8 views
11

Z PostgreSQL, możemy zrobić coś takiego:Jak używać Postgres' typ wyliczany z Ecto

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

Od Ecto's official doc, nie ma natywnego typu mapować postgres' typ wymienić. Ten module zapewnia niestandardowy typ dla wyliczonych struktur, ale odwzorowuje na liczbę całkowitą w bazie danych. Mogłabym z łatwością korzystać z tej biblioteki, ale wolałbym używać natywnego typu wyliczeniowego, który jest dostarczany z bazą danych.

Ecto zapewnia również sposób tworzenia custom types, ale o ile widzę, niestandardowy typ musi mapować do natywnego typu ekto ...

ktoś wie, czy można to zrobić w schemacie z Ecto ? Jeśli tak, w jaki sposób migracja zadziałałaby?

Odpowiedz

5

Musisz utworzyć typ Ecto dla każdego wyliczenia postgresql. W definicji schematu po prostu masz typ: :string. W przypadku migracji ustawiasz typ na nazwę modułu. To może się naprawdę uciążliwe, choć, więc mam następujące makro w moim projekcie, który używa PostgreSQL teksty stałe:

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 

Możliwe wykorzystanie:

import MyDB.Enum 
defenum ColorsEnum, "colors_enum", ~w"blue red yellow" 

ColorsEnum będzie nazwa modułu, "colors_enum" będzie nazwa wewnętrzna do Postgresql: musisz dodać instrukcję, aby utworzyć typ wyliczeniowy w swoich migracjach baz danych. Ostatnim argumentem jest lista wartości wyliczeniowych. Użyłem sigila ~w, które podzieli ciąg znaków na białe znaki, aby pokazać, jak zwięźle to może być. Dodałem także klauzulę, która konwertuje wartości atomów do wartości łańcuchów, gdy przechodzą przez schemat Ecto.

+0

Dzięki za odpowiedź! Wygląda to obiecująco, ale nie widzę sposobu użycia ColorsEnum w schemacie. (Dostaję część dotyczącą migracji). Kiedy dodaję pole do schematu, jakiego typu powinienem użyć? ': string'? –

+0

Tak, powinieneś użyć ': string' w rzeczywistej definicji schematu. Powód, dla którego typ Enum jest niezbędny, aby 'postgrex' robił swoje, polega na tym, że mapuje on wewnętrzny postgresql' oid' do typu Elixir, który jest w tym przypadku ciągiem. – asonge

+0

@Smith Podczas gdy szanuję twój Elixir Fu, ja osobiście nie potrzebuję typu Ecto dla każdego wyliczenia postgresql. Może brakuje mi czegoś, ale dlaczego miałbyś go potrzebować? Po prostu użyj ciągów znaków i walidatora zestawów zmian https://hexdocs.pm/ecto/Ecto.Changeset.html#validate_inclusion/4 –

22

Może zrobiłem coś złego, ale ja po prostu stworzył rodzaj i pole takiego:

# 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 

a potem po prostu wykonane polu ciąg:

field :post_status, :string 

i wydaje się działać.

+10

Dla tych, którzy są nowi w branży, rozwiązanie JustMichaela działa, ale pomyślałem, że dodam, gdzie kod musi iść. Pierwsza sekcja kodu znajduje się w pliku migracyjnym wewnątrz bloku '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' ''. Drugi blok przechodzi do pliku modelu wewnątrz bloku '' 'schemat do'''. – jeffreymatthias

+1

Co się dzieje, gdy przekazujesz ciąg, który nie jest "opublikowany" lub "edycja"? Jaki typ błędu występuje? –

+1

@DerenceChow DB najprawdopodobniej coś podniesie, a twoja operacja DB zawiedzie. – JustMichael

7

Mała korekta dla @JustMichael. Jeśli trzeba cofnąć, można użyć:

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

Tak! Słuszna uwaga! –

3

dodanie do co @JustMichael i @swennemen powiedzieć ... jak z ekto 2.2.6 mamy ekto .Migration.execute/2, która przyjmuje argument w górę i w dół. Tak więc możemy zrobić:

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

W naszym pliku migracji wewnątrz bloku change i ekto będzie mógł skutecznie cofnąć.