まとめ

  • validateはクエリ実行前のチェック、constraintは実行後のチェック
  • validate,constraintに書かれている知識ならectoはエラーを返すが、それ以外は例外になる
  • unique_constraintはindexの名前が規則通りじゃないとエラーにしてくれない
    • 規則から外れる場合はnameオプションで指定
  • primaryキーのconstraintnの書き方はこちら

実験

普通にinsertする

CREATE TABLE `people` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `first_name` varchar(255) NOT NULL,
  `last_name` varchar(255) DEFAULT NULL,
  `age` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `people_first_name_index` (`first_name`),
  UNIQUE KEY `last_name_index` (`last_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
defmodule Friends.Person do
  use Ecto.Schema

  schema "people" do
    field :first_name, :string
    field :last_name,  :string
    field :age,        :integer
  end
end
iex(1)> {:ok, row} = Friends.Repo.insert(%Friends.Person{})

01:32:25.191 [debug] QUERY OK db=2.1ms queue=5.7ms
INSERT INTO `people` () VALUES () []
{:ok,
 %Friends.Person{__meta__: #Ecto.Schema.Metadata<:loaded, "people">, age: nil,
   first_name: nil, id: 37, last_name: nil}}

validation

castはpersonとparamsの指定のキーワードを比較して差分だけchangesetにする。
validate_requiredはnilと空文字の禁止。デフォでtrimもされるので注意。

def changeset(person, params \\ %{}) do
  person
  |> Ecto.Changeset.cast(params, [:first_name, :last_name, :age])
  |> Ecto.Changeset.validate_required([:first_name, :last_name])
end

first_name,last_nameが空のchangesetを作成

iex(1)> iex(1)> changeset = Friends.Person.changeset(%Friends.Person{})
#Ecto.Changeset<action: nil, changes: %{},
 errors: [first_name: {"can't be blank", [validation: :required]},
   last_name: {"can't be blank", [validation: :required]}],
 data: #Friends.Person<>, valid?: false>
  • changeset.valid?はfalse
    • そのままinsertするとクエリ実行前にエラー
  • validate_requiredが存在しないとクエリが実行される
    • schemaがNOT NULLなので例外発生
iex(2)> {:error, _changest} = Friends.Repo.insert(changeset)
{:error,
 #Ecto.Changeset<action: :insert, changes: %{},
   errors: [first_name: {"can't be blank", [validation: :required]},
    last_name: {"can't be blank", [validation: :required]}],
   data: #Friends.Person<>, valid?: false>}

constraint

  • クエリ実行時にDB側でエラーになると、Elixirでは例外が発生するが、事前にconstraintで定義していた事項は例外ではなくエラーにすることができる。
  • unique_constraintではunique indexの名前をectoが知っている必要があるので、nameオプションで指定
    • デフォルトnameは#{table名}_#{field名}_index
def changeset(person, params \\ %{}) do
  person
  |> Ecto.Changeset.cast(params, [:first_name, :last_name, :age])
  |> Ecto.Changeset.validate_required([:first_name, :last_name])
  |> Ecto.Changeset.unique_constraint(:first_name)
  |> Ecto.Changeset.unique_constraint(:last_name, name: "last_name_index")
end

ecto経由のmigrationでDBに追加したindexならデフォルトで#{table名}_#{field名}_indexという名前のindex名になっている

defmodule Friends.Repo.Migrations.AddIndexPeople do
  use Ecto.Migration

  def change do
    create unique_index(:people, [:first_name])
  end
end
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account log in.