LoginSignup
17
9

More than 5 years have passed since last update.

ectoのchangesetを学ぶ

Last updated at Posted at 2018-03-26

まとめ

  • 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
17
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
17
9