LoginSignup
9
9

More than 5 years have passed since last update.

Ecto Getting Started (翻訳)

Last updated at Posted at 2016-10-24

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

同様に同じファイルで、ectopostgrex を 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.exstart/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_namelast_nameage がカラムとして追加されます。このフィールドの型は stringinteger です。(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_namelast_nameage フィールドと関連付けられます。

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.Repoinsert を呼び出します。このモジュールはデータベースを使用するのに 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.exFriends.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_namelast_name キーをキャストします。キャストはどのパラメーターが変更に含まれて良いかを決め、リストに含まれていないものは無視されます。

次の行では、validate_required を呼び出します。これは first_namelast_name が値を持っていることを期待します。この changeset を使って、first_namelast_name がない新しいレコードを作ろうとしてみましょう。

person = %Friends.Person{}
changeset = Friends.Person.changeset(person, %{})
Friends.Repo.insert(changeset)

最初の行では、Friends.Person モジュールから構造体を作成しています。少し前に見たばかりだから覚えているでしょう。次の行では changeset を定義するという新しいことをしています。この changeset は person オブジェクトの変更が作られるのを見ることができます。このケースでは、全く変更がないことがわかります。

最後の行では、person を挿入するのではなく、changeset を挿入しています。changesetperson とその変更、データベースにはデータが行く前に行われるべきバリデーションルールについて知っています。

{: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_namelast_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.Queryfrom を呼び出し、角カッコの間を渡します。ここで見ているように、Ecto.Query.first を使うことで orderlimit を使わなくても済みます。

このクエリを実行するためには、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 がなく、クエリに対して実行される limitorder 句もありません。デバッグログで実行されるクエリを見ることができます。

[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 は探しているものが明示的なクエリにのみ結果を返します。

クエリにマッチするレコードがない場合は、onenil を返します。

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"}}

insertupdate と同じように、delete はタプルを返します。削除が完了したらタプルの最初の要素は :ok、失敗したら :error になります。

9
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
9
9