https://hexdocs.pm/ecto/getting-started.html (Ecto v2.0.5) の翻訳になります。
訳が間違っている、抜けているなどの箇所などを見つけたら指摘していただけると助かります。
Getting Started
このガイドはElixirのデータベースラッパー及びクエリジェネレータEctoへのイントロダクションです。Ectoは、Elixirデベロッパーがどんなデータベースを使っていようと同じような方法でクエリによる問い合わせができるように、標準化されたAPIと抽象化された方法を提供しています。
このガイドでは、PostgreSQLデータベースのレコードの作成・読み込み・更新・削除のような基本的なことについて学べます。もしこのガイド中のコードを見たければ、ここ(at ecto/examples/friends on GitHub)のコードを見てください。
このガイドは事前にPostgreSQLがセットアップされているのを前提としています
Ectoのアプリケーションへの追加
手始めに、新しいElixirアプリケーションを作るため、以下のコマンドを使います。
mix new friends --sup
--sup
オプションはこのアプリケーションがa supervision treeを持っていること確認します。これは少し後で必要になります。
このアプリケーションにEctoを追加するのにいくつかのステップを踏む必要があります。最初のステップはmix.exs
ファイルの deps
を変更して、EctoとPostgrexというドライバを 追加することです。
defp deps do
[{:ecto, "~> 2.0"},
{:postgrex, "~> 0.11"}]
end
Ectoは一般的なクエリAPIを提供しますが、EctoがPostgreSQLデータベースが解釈できる語で話すためにPostgrexドライバを必要とします。
以上の依存ファイルをインストールするためには、以下のコマンドを使います。
mix deps.get
同様に同じファイルで、ecto
と postgrex
を applicationsのリストに追加します。
def application do
[applications: [:logger, :ecto, :postgrex],
mod: {Friends, []}]
end
PostgrexアプリケーションはEctoからクエリを受け取り、データベースに対してクエリを実行します。もしこのステップを踏まなければ、データベースに対する問い合わせはまったくできないでしょう。
二番目のステップに移りましょう。EctoとPostgrexを依存関係に組み込んだあとは、アプリケーションコードでデータベースを操作するためにいくつか設定が必要です。
この設定には以下のコマンドを使用します。
mix ecto.gen.repo -r Friends.Repo
このコマンドはデータベースと接続するための設定を生成します。config/config.exs
は以下のような設定になります。
config :friends, Friends.Repo,
adapter: Ecto.Adapters.Postgres,
database: "friends_repo",
username: "user",
password: "pass",
hostname: "localhost"
NOTE: PostgreSQLデータベースは
- ユーザー名・パスワードを必要としないようにセットアップすることもできます。もし上の設定でうまくいかないなら、username/password のフィールドを取り除くか、どちらも"postgres"とセットすれば良いです。
- 非標準的なポートで動作させるようにセットアップすることもできます。
port: 15432
のようにポート番号を指定すれば良いです。
この設定により "friends" と名付けたデータベースへどのように接続するかを決めることができます。厳密に言えば "repo" を設定している。より詳しい情報はEcto.Repo can be found in its documentationを参照すると良いです。
Friends.Repo
モジュールは mix ecto.gen.repo
コマンドで lib/friends/repo.ex
に定義されています。
defmodule Friends.Repo do
use Ecto.Repo, otp_app: :friends
end
このモジュールはデータベースに問い合わせをするために使うものです。このモジュールは Ecto.Repo
モジュールを使い、otp_app
はEctoにどのElixirアプリケーションのデータベースの設定を探すかを伝えます。今回のガイドの場合、Ectoが設定ファイルを見つけることができるアプリケーションを :friends
に指定しているのでEctoはセットアップ済みの設定を使うことができます。
この設定の最後のピースは、Friends.Repo
をスーパーバイザーとして監視ツリーにセットアップすることです。lib/friends.ex
の start/2
関数の中でセットアップはできます。
def start(_type, _args) do
import Supervisor.Spec, warn: false
children = [
supervisor(Friends.Repo, []),
]
...
この設定はEctoプロセスを開始させ、アプリケーションのクエリの受け取り及び実行することを可能にします。この設定なしではデータベースに問い合わせすることは全くできないでしょう!
最後にジェネレータが自動で加えてくれたりはしないので、自分自身をconfig/config.exs
に以下のコードを追加します。
config :friends, ecto_repos: [Friends.Repo]
このコードによって、mix ecto.create
などのコマンドを動作させる repo についてアプリケーションに伝えることができます。
以上の設定で、データベースに問い合わせを行うことができるようにアプリケーションを設定することができました。続いてデータベース・テーブルを作成して問い合わせを行ってみましょう。
データベースの設定
データベースへ問い合わせをするためには、データベースが存在してなければなりません。次のコマンドでデータベースを作成します。
mix ecto.create
もしデータベースがすでに作られているならば、次のメッセージが表示されます。
The database for Friends.Repo has been created.
NOTE: もしエラーになってしまったら、データベースへの認証が通るように config/config.exs
の設定を変更する必要があります。
データベースだけでは問い合わせすることができないので、次にテーブルを作ります。テーブルを作るには migration を行います。もしActive Recordやその類似品を知っているなら見たことがあるでしょう。マイグレーションはデータベースを構成する過程の一つのステップです。
マイグレーションは次のコマンドで作ることができます。
mix ecto.gen.migration create_people
このコマンドを実行すると priv/repo/migrations
に新しいマイグレーション用のファイルを作成します。デフォルトでは空ファイルになります。
defmodule Friends.Repo.Migrations.CreatePeople do
use Ecto.Migration
def change do
end
end
いくつかのカラムを持った "people" テーブルを作るには、このファイルにいくつかコードを追加します。
defmodule Friends.Repo.Migrations.CreatePeople do
use Ecto.Migration
def change do
create table(:people) do
add :first_name, :string
add :last_name, :string
add :age, :integer
end
end
end
この新しいコードによって people
と名付けた新しいテーブルを作るようにEctoに伝えることができます。first_name
、last_name
、age
がカラムとして追加されます。このフィールドの型は string
と integer
です。(Ectoがサポートする型はEcto.Schemaで見ることができます)
NOTE: Ectoデータベースのテーブルのネーミングルールは複数形です。
作成したマイグレーションを実行して people
テーブルを作るには次のコマンドが必要です。
mix ecto.migrate
マイグレーションにミスを見つけたときは、mix ecto.rollback
でそのマイグレーションの変更を打ち消すことができます。マイグレーションを修正したら再び mix ecto.migrate
を実行します。mix ecto.rollback
を一度実行してみると、作られたテーブルが削除されるのがわかるでしょう。
ここまででデータベースにテーブルを作成することができました。次のステップではスキーマを作成します。
スキーマの作成
スキーマはデータベースのデータをElixirで表現したものです。スキーマは一般的にデータベースのテーブルと関連付けられていますが、データベースのビューとも同様付けることができます。
lib/friends/person.ex
にスキーマを作ってみましょう。
defmodule Friends.Person do
use Ecto.Schema
schema "people" do
field :first_name, :string
field :last_name, :string
field :age, :integer
end
end
このコードでデータベースのスキーマと対応するスキーマを定義できます。この場合、Friends.Person
スキーマはデータベースの people
テーブル、first_name
・last_name
・age
フィールドと関連付けられます。
Ectoのネーミング変換は単数形のため、このスキーマを Person
と呼びます。
このスキーマは iex-S mix
で起動したIExセッションで試してみることができます。次のコードを実行してみましょう。
person = %Friends.Person{}
このコードで新しく Friends.Person
の構造を持ち各フィールドが nil
の構造体が得られます。これらのフィールドは新しく構造体を作ることで値をセットすることができます。
person = %Friends.Person{age: 28}
以下のような文法でも同様です。
%{person | age: 28}
構造体の値は以下の文法で得ることができます。
person.age # => 28
次にデータベースにどのようにデータを挿入するかを見ていきましょう。
データの挿入
people
テーブルに次のコードで新しいレコードを挿入できます。
person = %Friends.Person{}
Friends.Repo.insert(person)
データベースにデータを挿入するには、Friends.Repo
の insert
を呼び出します。このモジュールはデータベースを使用するのに Ecto を使用しています。この関数は Ecto に Friends.Repo
に対応するデータベースに 新しい Friends.Person
レコードを挿入したいことを伝えます。ここでの person
構造体はデータベースに挿入したいデータを表しています。
挿入が成功した場合は次のようなタプルを返します。
{:ok,
%Friends.Person{__meta__: #Ecto.Schema.Metadata<:loaded>, age: nil,
first_name: nil, id: 1, last_name: nil}}
:ok
アトムはデータ挿入が成功したことを確認するためにパターンマッチで使うことができます。挿入が失敗するシチュエーションはデータベース上で制約がある場合です。例えば、もしデータベースが email が一人のレコードのみに使われるようにするために、ユニーク制約を email
フィールドにかけていたら挿入は失敗するでしょう。
データベースに挿入したレコードを参照したい場合、タプルにパターンマッチするようにすると良いです。
{:ok, person} = Friends.Repo.insert person
変更の検証(バリデーション)
Ecto では、データベースに変更が伝わる前に、変更にバリデーションをかけたいと思うでしょう。例えば、データベースにレコードの情報が行く前に、一人の人が苗字と名前を持っていることを望むとしましょう。これを検証するために Ecto は changesets を持っています。
lib/friends/person.ex
の Friends.Person
モジュールに changeset を追加しましょう。
def changeset(person, params \\ %{}) do
person
|> Ecto.Changeset.cast(params, [:first_name, :last_name, :age])
|> Ecto.Changeset.validate_required([:first_name, :last_name])
end
changeset は person
とパラメーターセットを引数に取ります。このパラメーターセットは person に対して行いたい変更です。changeset
関数は渡されたパラメーターから first_name
と last_name
キーをキャストします。キャストはどのパラメーターが変更に含まれて良いかを決め、リストに含まれていないものは無視されます。
次の行では、validate_required
を呼び出します。これは first_name
と last_name
が値を持っていることを期待します。この changeset を使って、first_name
と last_name
がない新しいレコードを作ろうとしてみましょう。
person = %Friends.Person{}
changeset = Friends.Person.changeset(person, %{})
Friends.Repo.insert(changeset)
最初の行では、Friends.Person
モジュールから構造体を作成しています。少し前に見たばかりだから覚えているでしょう。次の行では changeset を定義するという新しいことをしています。この changeset は person
オブジェクトの変更が作られるのを見ることができます。このケースでは、全く変更がないことがわかります。
最後の行では、person
を挿入するのではなく、changeset
を挿入しています。changeset
は person
とその変更、データベースにはデータが行く前に行われるべきバリデーションルールについて知っています。
{:error,
#Ecto.Changeset<action: :insert, changes: %{},
errors: [first_name: "can't be blank", last_name: "can't be blank"],
data: #Friends.Person<>, valid?: false>}
最後に挿入を行ったときのように、このコードはタプルを返します。しかし、この場合は問題があること示すためにタプルの最初の要素は :error
になります。何が起こったかは返ってきた changeset に含まれています。これはパターンマッチをすることが取り出すことができます。
{:error, changeset} = Friends.Repo.insert(changeset)
changeset.errors
を行うことでエラーを得ることができます。
[first_name: "can't be blank", last_name: "can't be blank"]
挿入を行う前にでも、changeset
それ自体が正しいかを検証できます。
changeset.valid?
#=> false
この changeset はエラーを持っているので、people
テーブルには何も挿入されません。
正しいデータでも試してみましょう。
person = %Friends.Person{}
changeset = Friends.Person.changeset(person, %{first_name: "Ryan", last_name: "Bigg"})
普通の Friends.Person
構造体からはじめてみましょう。次に first_name
と last_name
パラメーターが指定された person
の changeset を作ることができます。この時点で changeset にエラーがあるかどうかを尋ねることができます。
changeset.errors
#=> []
そして、このデータが正しいかそうでないかを尋ねることができます。
changeset.valid?
#=> true
changeset はエラーを持たないため正しいことがわかります。このことから、この changeset を挿入しようとした場合、それは適切に動作します。
Friends.Repo.insert(changeset)
#=> {:ok,
%Friends.Person{__meta__: #Ecto.Schema.Metadata<:loaded>, age: nil,
first_name: "Ryan", id: 3, last_name: "Bigg"}}
Friends.Repo.insert
がタプルを返すので、case
を使うことで、何が起こったかによってどのコードを使用するか決めることができます。
case Friends.Repo.insert(changeset) do
{:ok, person} ->
# do something with person
{:error, changeset} ->
# do something with changeset
end
NOTE: changeset.valid?
は uniqueness_constraint のような制約のチェックは行いません。このため、データベースに挿入を行ってみてエラーをチェックすることが必要となります。このことから、実際にデータ挿入を行ってみて、Friends.Repo.insert
の戻り値のタプルを検証することが正しいエラーを得るためのベストプラクティスになるでしょう。挿入する前の changeset はアプリケーションレベルのバリデーションエラーしか含まないからです。
changeset の挿入が成功したら、結果に含まれる person
をどのようなことにでも利用できます。もし失敗したら、changeset を参照してそのエラーを見ましょう。失敗の場合、エンドユーザーにそれを表示したいかもしれません。changeset のエラーは次のようなキーワードのリストです。
[first_name: {"can't be blank", []},
last_name: {"can't be blank", []}]
タプルの最初の要素はバリデーションメッセージで、次の要素がバリデーションメッセージに対するオプションのキーワードのリストです。validate_required/3
バリデーションはオプションを返しませんが、他の validate_length/3
のようなメソッドは返します。bio
というバリデーションをしていたフィールドがあり、そのフィールドが15文字以上でなければいけないとしましょう。これに大しては次のコードが返されます。
[first_name: {"can't be blank", []},
last_name: {"can't be blank", []},
bio: {"should be at least %{count} characters", [count: 15]}]
人が見るのに優しい方法でメッセージを表示させるには、Ecto.Changeset.traverse_errors/2
を使うことができます。
traverse_errors(changeset, fn {msg, opts} ->
Enum.reduce(opts, msg, fn {key, value}, acc ->
String.replace(msg, "%{#{key}}", to_string(value))
end)
end)
これは上で表示されたエラーに対して次のコードを返します。
%{
first_name: ["can't be blank"],
last_name: ["can't be blank"],
bio: ["should be at least 15 characters"],
}
最後に言いたいことは、Friends.Repo.insert!/2
を使うことで例外を引き起こすことができるということです。もし、変更が正しくない場合、Ecto.InvalidChangesetError
例外が発生します。例を次に示します。
Friends.Repo.insert! Friends.Person.changeset(%Friends.Person{}, %{first_name: "Ryan"})
** (Ecto.InvalidChangesetError) could not perform insert because changeset is invalid.
* Changeset changes
%{first_name: "Ryan"}
* Changeset params
%{"first_name" => "Ryan"}
* Changeset errors
[last_name: "can't be blank"]
lib/ecto/repo/schema.ex:111: Ecto.Repo.Schema.insert!/4
この例外は変更と変更がどのように間違っているかを表示しています。これはデータを挿入したいときに役立ち、データが正しく挿入できない場合は例外を発生させます。
これでデータベースへのデータの挿入をすることができるようになりました。次にデータをどのように取り出すかを見ていきましょう。
最初のクエリ
Ectoによるデータベースにへの問い合わせは二つのステップを必要とします。クエリを組み立て、リポジトリにクエリを渡して実行します。これをする前に、アプリのためにデータベースを再作成し、いくつかのテストデータをセットアップしましょう。データベースを再作成するためには、次のコマンドを実行します。
mix ecto.drop
mix ecto.create
mix ecto.migrate
そして、テストデータを作るために、iex -S mix
セッションで次のコードを実行します。
people = [
%Friends.Person{first_name: "Ryan", last_name: "Bigg", age: 28},
%Friends.Person{first_name: "John", last_name: "Smith", age: 27},
%Friends.Person{first_name: "Jane", last_name: "Smith", age: 26},
]
Enum.each(people, fn (person) -> Friends.Repo.insert(person) end)
このコードはデータベースに、Ryan、John、Janeの三つの people を作成します。データを検証するのに changeset が使えますが、今回はしていません。
次のセクションでは people に足して問い合わせを行いましょう。さぁ次へ!
単一レコードの取り出し
people
テーブルから一つのレコードを取り出すことから始めましょう。
Friends.Person |> Ecto.Query.first
このコードは次のような Ecto.Query
を作り出します。
#Ecto.Query<from p in Friends.Person, order_by: [asc: p.id], limit: 1>
角カッコ <...>
の間のコードは組み立てられた Ecto クエリを表しています。次のようなほとんど同じようなシンタックスでもこのクエリを組み立てることができます。
require Ecto.Query
Ecto.Query.from p in Friends.Person, order_by: [asc: p.id], limit: 1
マクロを有効にするため require Ecto.Query
を必要とします。Ecto.Query
の from
を呼び出し、角カッコの間を渡します。ここで見ているように、Ecto.Query.first
を使うことで order
と limit
を使わなくても済みます。
このクエリを実行するためには、Friends.Repo.one
を呼び出します。
Friends.Person |> Ecto.Query.first |> Friends.Repo.one
one
関数はデータベースから一つだけレコードを取り出し、Friends.Person
モジュール内の新しい構造体を返します。
%Friends.Person{__meta__: #Ecto.Schema.Metadata<:loaded>, age: 28,
first_name: "Ryan", id: 1, last_name: "Bigg"}
first
も同様で、last
という関数もあります。
Friends.Person |> Ecto.Query.last |> Friends.Repo.one
#=> %Friends.Person{__meta__: #Ecto.Schema.Metadata<:loaded>, age: 26,
first_name: "Jane", id: 3, last_name: "Smith"}
Ecto.Repo.one
関数はデータベースのレコードが一つだけならば、一つ構造体を返します。一つ以上レコードがあったら、Ecto.MultipleResultsError
例外が投げられます。次のコードはその問題を引き起こします。
Friends.Person |> Friends.Repo.one
Ecto.Query.first
がなく、クエリに対して実行される limit
や order
句もありません。デバッグログで実行されるクエリを見ることができます。
[timestamp] [debug] SELECT p0."id", p0."first_name", p0."last_name", p0."age" FROM "people" AS p0 [] OK query=1.8ms
このすぐ後に Ecto.MultipleResultsError
例外が発生します。
** (Ecto.MultipleResultsError) expected at most one result but got 3 in query:
from p in Friends.Person
lib/ecto/repo/queryable.ex:67: Ecto.Repo.Queryable.one/4
これは Ecto が複数のレコードがあると探しているレコードが不明であるために起こります。Ecto は探しているものが明示的なクエリにのみ結果を返します。
クエリにマッチするレコードがない場合は、one
は nil
を返します。
Fetching all records
全レコードの取り出し
スキーマから全てのレコードを取り出すために、Ecto は all
関数を持っています。
Friends.Person |> Friends.Repo.all
これは、people
テーブルに現在入っているレコードを Friends.Person
構造体形式で返します。
[%Friends.Person{__meta__: #Ecto.Schema.Metadata<:loaded>, age: 28,
first_name: "Ryan", id: 1, last_name: "Bigg"},
%Friends.Person{__meta__: #Ecto.Schema.Metadata<:loaded>, age: 27,
first_name: "John", id: 2, last_name: "Smith"},
%Friends.Person{__meta__: #Ecto.Schema.Metadata<:loaded>, age: 26,
first_name: "Jane", id: 3, last_name: "Smith"}]
IDをもとにした単一レコードの取り出し
ID をもとにレコードを取り出すときは get
関数を使います。
Friends.Person |> Friends.Repo.get(1)
%Friends.Person{__meta__: #Ecto.Schema.Metadata<:loaded>, age: 28,
first_name: "Ryan", id: 1, last_name: "Bigg"}
特定の属性をもとにした単一レコードの取り出し
id
以外の属性をもとにレコードを取り出したい場合は、get_by
関数を使うことができます。
Friends.Person |> Friends.Repo.get_by(first_name: "Ryan")
%Friends.Person{__meta__: #Ecto.Schema.Metadata<:loaded>, age: 28,
first_name: "Ryan", id: 1, last_name: "Bigg"}
フィルタリング
特定の属性にマッチする複数のレコードを取り出すためには where
が使えます。
Friends.Person |> Ecto.Query.where(last_name: "Smith") |> Friends.Repo.all
[%Friends.Person{__meta__: #Ecto.Schema.Metadata<:loaded>, age: 27,
first_name: "John", id: 2, last_name: "Smith"},
%Friends.Person{__meta__: #Ecto.Schema.Metadata<:loaded>, age: 26,
first_name: "Jane", id: 3, last_name: "Smith"}]
もし、末尾の Friends.Repo.all
がない場合、Ecto が次のクエリを生成するのがわかるでしょう。
#Ecto.Query<from p in Friends.Person, where: p.last_name == "Smith">
同じレコードを取り出すのにこのクエリシンタックスを使うこともできます。
Ecto.Query.from(p in Friends.Person, where: p.last_name == "Smith") |> Friends.Repo.all
どちらのクエリシンタックスについてもピン演算子(^
)を使って、ピンされた変数を必要とします。
last_name = "Smith"
Friends.Person |> Ecto.Query.where(last_name: last_name) |> Friends.Repo.all
** (Ecto.Query.CompileError) variable `last_name` is not a valid query expression.
Variables need to be explicitly interpolated in queries with ^
expanding macro: Ecto.Query.where/2
iex:1: (file)
(elixir) expanding macro: Kernel.|>/2
iex:1: (file)
より長いクエリシンタックスでも同じことが起こります。
Ecto.Query.from(p in Friends.Person, where: p.last_name == last_name) |> Friends.Repo.all
** (Ecto.Query.CompileError) variable `last_name` is not a valid query expression.
Variables need to be explicitly interpolated in queries with ^
expanding macro: Ecto.Query.where/3
iex:1: (file)
expanding macro: Ecto.Query.from/2
iex:1: (file)
(elixir) expanding macro: Kernel.|>/2
iex:1: (file)
これを実行してデータを取り出すには、ピン演算子(^
)を使います。
last_name = "Smith"
Friends.Person |> Ecto.Query.where(last_name: ^last_name) |> Friends.Repo.all
もしくは以下のように、
last_name = "Smith"
Ecto.Query.from(p in Friends.Person, where: p.last_name == ^last_name) |> Friends.Repo.all
ピン演算子はクエリビルダーにSQLインジェクション対策が施されたパラメーター化されたSQLクエリを使うようにします。
Ectoクエリの組み立て
Ectoクエリは一つの場所で組み立てる必要はありません。すでに存在するクエリから Ecto.Query
関数を呼び出すことでクエリを作ることができます。例えば、名前が "Smith" である全ての人を見つけたいとしましょう。次のようにできます。
query = Friends.Person |> Ecto.Query.where(last_name: "Smith")
苗字が "Jane" の人だけにスコープを絞りたいならば、次のようにできます。
query = query |> Ecto.Query.where(first_name: "Jane")
クエリはこの時点で二つの where
句を持つことになります。
#Ecto.Query<from p in Friends.Person, where: p.last_name == "Smith",
where: p.first_name == "Jane">
これは最初のクエリで何か作業をしてから、その後でそのクエリを使って別なクエリを構築するのに役立ちます。
レコードの更新
レコードを Ecto で更新するには、最初にデータベースからレコードを取り出す必要があります。その後で、レコードから changeset とレコードに対する変更を構成し、Ecto.Repo.update
関数を呼び出します。
データベースから最初の person レコードを取り出し、年齢(age)を変更してみましょう。最初に person を取り出します。
person = Friends.Person |> Ecto.Query.first |> Friends.Repo.one
次に、changeset を作ります。単に新しい年齢(age)を持った新しい Friends.Person
構造体を作るので、changeset が必要になります。Ecto はデータベースとの連携なしでは年齢が変更されるかを知ることができないかもしれません。changeset を作ってみましょう。
changeset = Friends.Person.changeset(person, %{age: 29})
changeset はデータベースに年齢が29にするためにレコードを更新したいことを知らせます。変更についてデータベースとやり取りするために、次のコマンドを実行します。
Friends.Repo.update(changeset)
Friends.Repo.insert
と同じように、Friends.Repo.update
はタプルを返します。
{:ok,
%Friends.Person{__meta__: #Ecto.Schema.Metadata<:loaded>, age: 29,
first_name: "Ryan", id: 1, last_name: "Bigg"}}
何かの理由で changeset が失敗したら、Friends.Repo.update
の結果は {:error, changeset}
になります。これは first_name
が空になるように changeset のパラメーターを変えることで引き起こすこともできます。
changeset = Friends.Person.changeset(person, %{first_name: ""})
#=> {:error,
#Ecto.Changeset<action: :update, changes: %{first_name: ""},
errors: [first_name: "can't be blank"], data: #Friends.Person<>,
valid?: false>}
このことは update
関数からの結果によって異なったコードを実行するのに、case
ステートメントを使えるということを意味しています。
case Friends.Repo.update(changeset) do
{:ok, person} ->
# do something with person
{:error, changeset} ->
# do something with changeset
end
insert!
と同じように、changeset に問題がある場合に例外を引き起こす update!
関数があります。
changeset = Friends.Person.changeset(person, %{first_name: ""})
Friends.Repo.update! changeset
** (Ecto.InvalidChangesetError) could not perform update because changeset is invalid.
* Changeset changes
%{first_name: ""}
* Changeset params
%{"first_name" => ""}
* Changeset errors
[first_name: {"can't be blank", []}]
lib/ecto/repo/schema.ex:132: Ecto.Repo.Schema.update!/4
レコードの削除
レコードの作成(insert
)・読み込み(get
, get_by
, where
)・更新についてカバーしました。このガイドの最後に行ったのは Ecto を使ったレコードの削除についてでした。
更新と同じように、レコードを削除するには、最初にデータベースからレコードを取り出し、Friends.Repo.delete
を呼び出します。
person = Friends.Repo.get(Friends.Person, 1)
Friends.Repo.delete(person)
{:ok,
%Friends.Person{__meta__: #Ecto.Schema.Metadata<:deleted>, age: 29,
first_name: "Ryan", id: 2, last_name: "Bigg"}}
insert
や update
と同じように、delete
はタプルを返します。削除が完了したらタプルの最初の要素は :ok
、失敗したら :error
になります。