Eto 3.5で新しいenum型を使用してください


Booleansはプログラミングで最も古い構文のうちの1つです.彼らは簡単です.バイナリ.そして、我々はどこでもBooleansを使います.私の最も初期のソフトウェアプロジェクトの1つは、「ユーザー」と呼ばれるデータベーステーブルを持っていましたis_admin , is_moderator , and is_seller .
%User{is_admin: false, is_moderator: false, is_seller: false}
これは、booleansが若干の状態の状態を表すのに用いられるときさらに、より悪くなります.例えば、Booleansでその状態を保存する支払いサービスは、潜在的にコラムを持つかもしれませんis_authorized , is_captured , is_failed , and is_refunded . それはちょっと複雑だ.そして、それはちょうど1つの州の状態です.多くの場所は、現在の別のブールを持っていることを意味部分的な払い戻しをサポートしていますis_partial_refund .
%Payment{
  is_authorized: false,
  is_captured: false,
  is_failed: false,
  is_refunded: false,
  is_partial_refund: false
}
しかし、別の問題があります:今、あなたは不可能な状態を持つことができます.例えば、何らかの記録があればis_partial_refund 設定するtrue でもis_refunded 設定するfalse それはどういう意味ですか.またはレコードがis_failed 設定するtrue でもis_captured 設定するtrue . 不可能な状態を不可能にする!

会館
enums私たちはそれを行うことができます.変数は、定義済み定数の集合から値を格納することができます.静的にタイプされた言語では、enum値はコンパイル時にチェックされ、悪い値を根絶します.Elixirは動的にタイプされるので、我々はその安全保証を得ません、しかし、それはenumsの有用性を破壊しません.
上記のユーザ例はenumを使用して簡単になります.
%User{role: :admin}
ロールフィールドには、次のいずれかの原子が含まれます:admin , :moderator , :seller , :buyer . それは非常に私たちのユーザーstructを簡素化します、しかし、違いは支払い例でよりさらに大きいです:
%Payment{status: :pending}
つのブールフラグの別の組み合わせを設定する代わりに、我々は1つのステータスに変更することができます:authorized , :captured , :failed , :refunded , or :partially_refunded . そして、支払いが上記の不可能な状態の1つに入る方法がありません.しかし、どのようにこれはデータベースで動作しますか?

文字列で独自のenums圧延
ElxirとEtoとデータベースに“enums”を格納する最も簡単な方法は、独自のenumsをロールバックされています.テーブルに新しいVarchar列を作成することで、レコードを作成または更新するときに適切な文字列を書き込むことができます.Ecto.Changeset.validate_inclusion/4 提供されている文字列がサポートされている値のいずれかを含んでいることを確認できます.
しかし、その安全性はアプリケーション層にしか存在しない.データベースに直接アクセスする誰かがアプリケーションをバイパスすることで悪い値を入力する可能性を無視します.悪いストリングが入って、アプリケーションがそれを扱わないならば、その悪いストリングはプロセスを壊します.しかし、データ層から来る値を検証する時間を無駄にする必要はありません.あなたのアプリケーションは、より良いに値する.

外胚葉
PostgresとMySQLは、列に使用できるEnum型を作成する機能を持ち、アプリケーション層ではなくデータ層でそれらを検証します.これはデータストアが悪い値から保護されることを意味します.For
お久しぶりですEctoEnum library Postgresのカスタムenum型を設定する最良の方法です.
# lib/my_app/accounts/user_role.ex
defmodule MyApp.Accounts.UserRole do
  use EctoEnum, type: :user_role, enums: [:admin, :moderator, :seller, :buyer]
end

# lib/my_app/accounts/user.ex
defmodule MyApp.Accounts.User do
  use Ecto.Schema
  alias MyApp.Accounts.UserRole

  schema "users" do
    field :role, UserRole
  end
end

# priv/repo/migrations/20210102193646_add_role_to_users.exs
defmodule MyApp.Repo.Migrations.AddRoleToUsers do
  use Ecto.Migration
  alias MyApp.Accounts.UserRole

  def change do
    UserRole.create_type()

    alter table(:users) do
      add :role, :user_role
    end
  end
end
これは、enum型のための新しいモジュールを作成する必要がありますが、それはあなたのスキーマの型を使用して、最初の場所での移行の型を作成するためのいくつかの良い便利さを持っています.しかし、もう一つの依存関係を使うことは、維持されなければならないアプリケーションのもう一つのことを意味します.

ECTO 3.5
ECTO 3.5は2020年10月にリリースされました Ecto.Enum モジュールです.モジュールのドキュメントでは、データベースに格納されているときにenum型が文字列であることを期待していますが、Postgres enumで動作させることもできます.つまり、別のライブラリを必要とせずにenumsを使うことができます.Ecto SQL まだ移行においてenumを作成するための便利な関数を持っていません.
defmodule MyApp.Repo.Migrations.AddRoleToUsers do
  use Ecto.Migration

  def up do
    execute("CREATE TYPE user_role AS ENUM ('admin', 'moderator', 'seller', 'buyer')")

    alter table(:users) do
      add :role, :user_role
    end
  end

  def down do
    alter table(:users) do
      remove :role, :user_role
    end

    execute("DROP TYPE user_role")
  end
end
次に、新しいモジュールとecto型を作成することなく、スキーマにenumを追加できます.
defmodule MyApp.Accounts.User do
  use Ecto.Schema

  schema "users" do
    field :role, Ecto.Enum, values: [:admin, :moderator, :seller, :buyer]
  end
end
ectoenumライブラリのように、ectoはchangesetsのためのキャスティングのすべてを扱います、そして、役割はストリングよりむしろ原子として利用できます.これはいくつかの安全性を提供します
ユーザーが安全な文字列の値と、アプリケーションによって検証されている安全な原子を提供します.

あなたはどう思いますか.
閉じるこの動画はお気に入りから削除されています.ENUM API、あるいはAPIが異なっていたらいいですか?あなたはその棒に固執するつもりですかEctoEnum ライブラリまたは単に文字列を使用するには?
閉じるこの動画はお気に入りから削除されています.Subscribe to my email newsletter そして、私が新しいものを書くときはいつでも、通知を得てください!
春節をお祝い申し上げます