An open API service indexing awesome lists of open source software.

https://github.com/miolab/ecto_postgres

EctoとPostgreSQLでいろいろ試すリポジトリ(IExベース)
https://github.com/miolab/ecto_postgres

ecto-db elixir iex postgresql

Last synced: about 2 months ago
JSON representation

EctoとPostgreSQLでいろいろ試すリポジトリ(IExベース)

Awesome Lists containing this project

README

          

# Ecto & PostgreSQL

`Ecto` と `PostgreSQL` でいろいろお試しします。

- __Ecto__

`Elixir`の
- Databaseラッパー

- クエリジェネレーター

- 参考

- [Hex](https://hex.pm/packages/ecto_sql)

- [Hexdocs](https://hexdocs.pm/ecto/getting-started.html)

---

## 実行環境

- macOS

```bash
$ psql --version
psql (PostgreSQL) 12.2

$ elixir --version
Erlang/OTP 22 [erts-10.5.3] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [hipe]

Elixir 1.9.2 (compiled with Erlang/OTP 22)
```

---

## EctoでDB作成

Hexdocsを元に実行します。

- __bash__

```bash
$ mix new friends --sup

* creating README.md
* creating .formatter.exs
* creating .gitignore
* creating mix.exs
* creating lib
* creating lib/friends.ex
* creating lib/friends/application.ex
* creating test
* creating test/test_helper.exs
* creating test/friends_test.exs

Your Mix project was created successfully.
You can use "mix" to compile it, test it, and more:

cd friends
mix test

Run "mix help" for more commands.
```

```bash
cd friends
```

- mix.exs

```elixir
defp deps do
[
{:ecto_sql, "~> 3.0"}, -> add
{:postgrex, ">= 0.0.0"} -> add
```

- __bash__

```bash
$ mix deps.get

Resolving Hex dependencies...
Dependency resolution completed:
New:
connection 1.0.4
db_connection 2.2.2
decimal 1.8.1
ecto 3.4.4
ecto_sql 3.4.3
postgrex 0.15.4
telemetry 0.4.1
* Getting ecto_sql (Hex package)
* Getting postgrex (Hex package)
* Getting connection (Hex package)
* Getting db_connection (Hex package)
* Getting decimal (Hex package)
* Getting ecto (Hex package)
* Getting telemetry (Hex package)
```

```bash
$ mix ecto.gen.repo -r Friends.Repo

==> connection
Compiling 1 file (.ex)
Generated connection app
===> Compiling telemetry
==> decimal
Compiling 1 file (.ex)
Generated decimal app
==> db_connection
Compiling 14 files (.ex)
Generated db_connection app
==> ecto
Compiling 55 files (.ex)
Generated ecto app
==> postgrex
Compiling 61 files (.ex)
Generated postgrex app
==> ecto_sql
Compiling 26 files (.ex)
Generated ecto_sql app
==> friends
* creating lib/friends
* creating lib/friends/repo.ex
* creating config/config.exs
Don't forget to add your new repo to your supervision tree
(typically in lib/friends/application.ex):

{Friends.Repo, []}

And to add it to the list of ecto repositories in your
configuration files (so Ecto tasks work as expected):

config :friends,
ecto_repos: [Friends.Repo]
```

- config/config.exs

```elixir
config :friends, Friends.Repo,
database: "friends_repo",
username: "postgres",
password: "postgres",
hostname: "localhost"

config :friends, ecto_repos: [Friends.Repo] --> add
```

- lib/friends/application

```elixir
def start(_type, _args) do
children = [
Friends.Repo, --> add
```

- __bash__

```bash
$ mix ecto.create

Compiling 3 files (.ex)
Generated friends app
The database for Friends.Repo has been created
```

#### 結果確認(DB)

```postgres
$ psql -l
List of databases
Name | Owner | Encoding | Collate | Ctype | Access privileges
----------------------+----------+----------+-------------+-------------+-------------------
friends_repo | postgres | UTF8 | ja_JP.UTF-8 | ja_JP.UTF-8 |
.
.
.
```

- Ectoでの __DBの作成__ に成功しました。

---

## DBセットアップ(マイグレーション 〜 テーブル作成)

- __bash__

```bash
$ mix ecto.gen.migration create_people

* creating priv/repo/migrations
* creating priv/repo/migrations/20200517041113_create_people.exs
```

- priv/repo/migrations/20200517041113_create_people.exs

```elixir
def change do
create table(:people) do --> add
add :first_name, :string --> add
add :last_name, :string --> add
add :age, :integer --> add
end
```

- __bash__

```bash
$ mix ecto.migrate

13:28:21.372 [info] == Running 20200517041113 Friends.Repo.Migrations.CreatePeople.change/0 forward

13:28:21.373 [info] create table people

13:28:21.382 [info] == Migrated 20200517041113 in 0.0s
```

#### 結果確認(テーブル)

```postgres
$ psql friends_repo
psql (12.2)
Type "help" for help.

friends_repo=# \dt
List of relations
Schema | Name | Type | Owner
--------+-------------------+-------+----------
public | people | table | postgres
public | schema_migrations | table | postgres
(2 rows)

friends_repo=# \d people
Table "public.people"
Column | Type | Collation | Nullable | Default
------------+------------------------+-----------+----------+------------------------------------
id | bigint | | not null | nextval('people_id_seq'::regclass)
first_name | character varying(255) | | |
last_name | character varying(255) | | |
age | integer | | |
Indexes:
"people_pkey" PRIMARY KEY, btree (id)

```

- Ectoでの __DBテーブル作成__ に成功しました。

- (メモ)

- マイグレーションでミスがあった場合、`mix ecto.rollback` で変更を元に戻すことが可能。
(その後、変更修正してから、再度 `mix ecto.create`を実行する)

- この段階で `mix ecto.rollback` すると、いま作成したばかりのテーブルを削除可能。

---

## レコード作成(Create)

- `insert()`

### スキーマ作成

- ファイル作成: `lib/friends/person.ex`

```elixir
defmodule Friends.Person do
use Ecto.Schema

schema "people" do
field :first_name, :string
field :last_name, :string
field :age, :integer
end
end
```

- `$ iex -S mix` で検証

```elixir
iex(1)> person = %Friends.Person{}
%Friends.Person{
__meta__: #Ecto.Schema.Metadata<:built, "people">,
age: nil,
first_name: nil,
id: nil,
last_name: nil
}

iex(2)> person = %Friends.Person{age: 28}
%Friends.Person{
__meta__: #Ecto.Schema.Metadata<:built, "people">,
age: 28,
first_name: nil,
id: nil,
last_name: nil
}

iex(3)> person.age
28

iex(4)> Friends.Repo.insert(person)

15:42:21.463 [debug] QUERY OK db=2.9ms decode=1.2ms queue=1.1ms idle=27.4ms
INSERT INTO "people" ("age") VALUES ($1) RETURNING "id" [28]
{:ok,
%Friends.Person{
__meta__: #Ecto.Schema.Metadata<:loaded, "people">,
age: 28,
first_name: nil,
id: 1,
last_name: nil
}}
```

### バリデーション機能実装

- `lib/friends/person.ex` の `defmodule` 内に、以下を追加

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

- `$ iex -S mix` で検証 _(バリデーションエラー パターン)_

```elixir
iex(1)> person = %Friends.Person{}
%Friends.Person{
__meta__: #Ecto.Schema.Metadata<:built, "people">,
age: nil,
first_name: nil,
id: nil,
last_name: nil
}

iex(2)> changeset = Friends.Person.changeset(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
>

iex(3)> 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
>}

iex(4)> changeset.valid?
false
```

- ちゃんと `:error` が返り、バリデーションが効いていることを確認できました。

- `$ iex -S mix` で検証 _(サクセス パターン)_

データの `INSERT` (CREATE処理) まで合わせて実行します。

```
iex(5)> person = %Friends.Person{}
%Friends.Person{
__meta__: #Ecto.Schema.Metadata<:built, "people">,
age: nil,
first_name: nil,
id: nil,
last_name: nil
}

iex(6)> changeset = Friends.Person.changeset(person, %{first_name: "im", last_name: "miolab"})
#Ecto.Changeset<
action: nil,
changes: %{first_name: "im", last_name: "miolab"},
errors: [],
data: #Friends.Person<>,
valid?: true
>

iex(7)> changeset.errors
[]

iex(8)> changeset.valid?
true

iex(9)> Friends.Repo.insert(changeset)

16:15:47.461 [debug] QUERY OK db=1.7ms decode=1.1ms queue=1.4ms idle=421.1ms
INSERT INTO "people" ("first_name","last_name") VALUES ($1,$2) RETURNING "id" ["im", "miolab"]
{:ok,
%Friends.Person{
__meta__: #Ecto.Schema.Metadata<:loaded, "people">,
age: nil,
first_name: "im",
id: 2,
last_name: "miolab"
}}
```

#### 結果確認(テーブル)

```postgres
$ psql friends_repo
psql (12.2)
Type "help" for help.

friends_repo=# select * from people;
id | first_name | last_name | age
----+------------+-----------+-----
1 | | | 28
2 | im | miolab |
(2 rows)
```

- Ectoでの __CREATE__ 処理に成功しました。

- `id: 2` レコードで、`first_name` `last_name` のバリデーションも効いています。

---

## クエリ実行

- 事前準備として、DBを再作成します。

- PostgreSQL

```postgres
# exit
```

- __bash__

```bash
$ mix ecto.drop
The database for Friends.Repo has been dropped

$ mix ecto.create
The database for Friends.Repo has been created

$ mix ecto.migrate

08:14:06.052 [info] == Running 20200517041113 Friends.Repo.Migrations.CreatePeople.change/0 forward

08:14:06.055 [info] create table people

08:14:06.078 [info] == Migrated 20200517041113 in 0.0s
```

- PostgreSQL

```postgres
$ psql friends_repo

friends_repo=# select * from people;
id | first_name | last_name | age
----+------------+-----------+-----
(0 rows)
```

DBが再作成されました。

- `$ iex -S mix` で以下を実行。

クエリを構築 → クエリをリポジトリに渡す → DBに対してクエリを実行

```elixir
iex(1)> people = [
...(1)> %Friends.Person{first_name: "im", last_name: "miolab", age: 28},
...(1)> %Friends.Person{first_name: "foo", last_name: "miolab", age: 27},
...(1)> %Friends.Person{first_name: "foobar", last_name: "hogehoge", age: 26},
...(1)> %Friends.Person{first_name: "eli", last_name: "xir", age: 26},
...(1)> ]
[
%Friends.Person{
__meta__: #Ecto.Schema.Metadata<:built, "people">,
age: 28,
first_name: "im",
id: nil,
last_name: "miolab"
},
%Friends.Person{
__meta__: #Ecto.Schema.Metadata<:built, "people">,
age: 27,
first_name: "foo",
id: nil,
last_name: "miolab"
},
%Friends.Person{
__meta__: #Ecto.Schema.Metadata<:built, "people">,
age: 26,
first_name: "foobar",
id: nil,
last_name: "hogehoge"
},
%Friends.Person{
__meta__: #Ecto.Schema.Metadata<:built, "people">,
age: 26,
first_name: "eli",
id: nil,
last_name: "xir"
}
]

iex(2)> Enum.each(people, fn(person) -> Friends.Repo.insert(person) end)

10:17:19.023 [debug] QUERY OK db=2.3ms decode=1.0ms queue=2.6ms idle=1198.7ms
INSERT INTO "people" ("age","first_name","last_name") VALUES ($1,$2,$3) RETURNING "id" [28, "im", "miolab"]

10:17:19.027 [debug] QUERY OK db=1.1ms queue=1.1ms idle=1208.3ms
INSERT INTO "people" ("age","first_name","last_name") VALUES ($1,$2,$3) RETURNING "id" [27, "foo", "miolab"]

10:17:19.030 [debug] QUERY OK db=1.0ms queue=0.9ms idle=1210.7ms
INSERT INTO "people" ("age","first_name","last_name") VALUES ($1,$2,$3) RETURNING "id" [26, "foobar", "hogehoge"]

10:17:19.032 [debug] QUERY OK db=1.0ms queue=0.9ms idle=1212.8ms
INSERT INTO "people" ("age","first_name","last_name") VALUES ($1,$2,$3) RETURNING "id" [26, "eli", "xir"]
:ok
```

(※ `changeset` 不経由)

#### 結果確認(テーブル)

```postgres
friends_repo=# select * from people;

id | first_name | last_name | age
----+------------+-----------+-----
1 | im | miolab | 28
2 | foo | miolab | 27
3 | foobar | hogehoge | 26
4 | eli | xir | 26
(4 rows)
```

---

## レコード取得(Read)

- `get()`
- `get_by()`
- `where()`

`$ iex -S mix` で確認。

- 最初のレコード 取得

```elixir
iex(1)> Friends.Person |> Ecto.Query.first |> Friends.Repo.one

11:17:20.928 [debug] QUERY OK source="people" db=0.8ms decode=1.6ms queue=1.5ms idle=1791.5ms
SELECT p0."id", p0."first_name", p0."last_name", p0."age" FROM "people" AS p0 ORDER BY p0."id" LIMIT 1 []
%Friends.Person{
__meta__: #Ecto.Schema.Metadata<:loaded, "people">,
age: 28,
first_name: "im",
id: 1,
last_name: "miolab"
}
```

- 最後のレコード 取得

```elixir
iex(2)> Friends.Person |> Ecto.Query.last |> Friends.Repo.one

11:17:46.505 [debug] QUERY OK source="people" db=1.5ms queue=2.5ms idle=1377.0ms
SELECT p0."id", p0."first_name", p0."last_name", p0."age" FROM "people" AS p0 ORDER BY p0."id" DESC LIMIT 1 []
%Friends.Person{
__meta__: #Ecto.Schema.Metadata<:loaded, "people">,
age: 26,
first_name: "eli",
id: 4,
last_name: "xir"
}
```

- __全レコード__ 取得

```elixir
iex(3)> Friends.Person |> Friends.Repo.all

11:19:58.105 [debug] QUERY OK source="people" db=0.3ms queue=0.4ms idle=1981.1ms
SELECT p0."id", p0."first_name", p0."last_name", p0."age" FROM "people" AS p0 []
[
%Friends.Person{
__meta__: #Ecto.Schema.Metadata<:loaded, "people">,
age: 28,
first_name: "im",
id: 1,
last_name: "miolab"
},
%Friends.Person{
__meta__: #Ecto.Schema.Metadata<:loaded, "people">,
age: 27,
first_name: "foo",
id: 2,
last_name: "miolab"
},
%Friends.Person{
__meta__: #Ecto.Schema.Metadata<:loaded, "people">,
age: 26,
first_name: "foobar",
id: 3,
last_name: "hogehoge"
},
%Friends.Person{
__meta__: #Ecto.Schema.Metadata<:loaded, "people">,
age: 26,
first_name: "eli",
id: 4,
last_name: "xir"
}
]
```

- __`id` を指定__ してレコード取得

```elixir
iex(4)> Friends.Person |> Friends.Repo.get(1)

11:21:08.927 [debug] QUERY OK source="people" db=1.2ms queue=1.6ms idle=1801.0ms
SELECT p0."id", p0."first_name", p0."last_name", p0."age" FROM "people" AS p0 WHERE (p0."id" = $1) [1]
%Friends.Person{
__meta__: #Ecto.Schema.Metadata<:loaded, "people">,
age: 28,
first_name: "im",
id: 1,
last_name: "miolab"
}

iex(5)> Friends.Person |> Friends.Repo.get(2)

11:21:14.877 [debug] QUERY OK source="people" db=3.9ms idle=1749.7ms
SELECT p0."id", p0."first_name", p0."last_name", p0."age" FROM "people" AS p0 WHERE (p0."id" = $1) [2]
%Friends.Person{
__meta__: #Ecto.Schema.Metadata<:loaded, "people">,
age: 27,
first_name: "foo",
id: 2,
last_name: "miolab"
}
```

- __カラム属性__ に基づいてレコードを取得

```elixir
iex(6)> Friends.Person |> Friends.Repo.get_by(first_name: "im")

11:23:40.635 [debug] QUERY OK source="people" db=1.4ms queue=5.4ms idle=1504.6ms
SELECT p0."id", p0."first_name", p0."last_name", p0."age" FROM "people" AS p0 WHERE (p0."first_name" = $1) ["im"]
%Friends.Person{
__meta__: #Ecto.Schema.Metadata<:loaded, "people">,
age: 28,
first_name: "im",
id: 1,
last_name: "miolab"
}
```

- フィルタリング抽出(`Ecto.Query` 併用)

```elixir
iex(7)> require Ecto.Query
Ecto.Query

iex(8)> Friends.Person |> Ecto.Query.where(last_name: "miolab") |> Friends.Repo.all

11:34:11.847 [debug] QUERY OK source="people" db=0.5ms queue=1.5ms idle=1721.2ms
SELECT p0."id", p0."first_name", p0."last_name", p0."age" FROM "people" AS p0 WHERE (p0."last_name" = 'miolab') []
[
%Friends.Person{
__meta__: #Ecto.Schema.Metadata<:loaded, "people">,
age: 28,
first_name: "im",
id: 1,
last_name: "miolab"
},
%Friends.Person{
__meta__: #Ecto.Schema.Metadata<:loaded, "people">,
age: 27,
first_name: "foo",
id: 2,
last_name: "miolab"
}
]
```

- 別記法

```elixir
iex(9)> Ecto.Query.from(p in Friends.Person, where: p.last_name == "miolab") |> Friends.Repo.all

12:02:34.174 [debug] QUERY OK source="people" db=0.9ms queue=0.1ms idle=1563.1ms
SELECT p0."id", p0."first_name", p0."last_name", p0."age" FROM "people" AS p0 WHERE (p0."last_name" = 'miolab') []
[
%Friends.Person{
__meta__: #Ecto.Schema.Metadata<:loaded, "people">,
age: 28,
first_name: "im",
id: 1,
last_name: "miolab"
},
%Friends.Person{
__meta__: #Ecto.Schema.Metadata<:loaded, "people">,
age: 27,
first_name: "foo",
id: 2,
last_name: "miolab"
}
]
```

- _Note_ : 変数をクエリ内で展開する場合は、`^` (ピン演算子)が必要。

```elixir
iex(10)> last_name_miolab = "miolab"
"miolab"

iex(11)> Friends.Person |> Ecto.Query.where(last_name: ^last_name_miolab) |> Friends.Repo.all

11:52:23.130 [debug] QUERY OK source="people" db=1.3ms queue=2.7ms idle=1518.1ms
SELECT p0."id", p0."first_name", p0."last_name", p0."age" FROM "people" AS p0 WHERE (p0."last_name" = $1) ["miolab"]
[
%Friends.Person{
__meta__: #Ecto.Schema.Metadata<:loaded, "people">,
age: 28,
first_name: "im",
id: 1,
last_name: "miolab"
},
%Friends.Person{
__meta__: #Ecto.Schema.Metadata<:loaded, "people">,
age: 27,
first_name: "foo",
id: 2,
last_name: "miolab"
}
]

iex(12)> Ecto.Query.from(p in Friends.Person, where: p.last_name == ^last_name_miolab) |> Friends.Repo.all

12:03:02.104 [debug] QUERY OK source="people" db=2.0ms idle=1493.0ms
SELECT p0."id", p0."first_name", p0."last_name", p0."age" FROM "people" AS p0 WHERE (p0."last_name" = $1) ["miolab"]
[
%Friends.Person{
__meta__: #Ecto.Schema.Metadata<:loaded, "people">,
age: 28,
first_name: "im",
id: 1,
last_name: "miolab"
},
%Friends.Person{
__meta__: #Ecto.Schema.Metadata<:loaded, "people">,
age: 27,
first_name: "foo",
id: 2,
last_name: "miolab"
}
]
```

- クエリのアレンジ(使用例)

`last_name` 抽出に、`first_name` 抽出を追加して `AND` 検索となるようクエリをアレンジします。

```elixir
iex(13)> query = Friends.Person |> Ecto.Query.where(last_name: "miolab")
#Ecto.Query

iex(14)> query_fullname = query |> Ecto.Query.where(first_name: "im")
#Ecto.Query

iex(15)> query_fullname |> Friends.Repo.all

12:20:26.074 [debug] QUERY OK source="people" db=0.2ms idle=1460.8ms
SELECT p0."id", p0."first_name", p0."last_name", p0."age" FROM "people" AS p0 WHERE (p0."last_name" = 'miolab') AND (p0."first_name" = 'im') []
[
%Friends.Person{
__meta__: #Ecto.Schema.Metadata<:loaded, "people">,
age: 28,
first_name: "im",
id: 1,
last_name: "miolab"
}
]
```

---

## レコード更新(Update)

- `update()`

`$ iex -S mix` で確認。

- iex

```elixir
iex(1)> require Ecto.Query
Ecto.Query

iex(2)> person = Friends.Person |> Ecto.Query.first |> Friends.Repo.one

13:09:01.291 [debug] QUERY OK source="people" db=0.9ms decode=1.5ms queue=1.3ms idle=1885.6ms
SELECT p0."id", p0."first_name", p0."last_name", p0."age" FROM "people" AS p0 ORDER BY p0."id" LIMIT 1 []
%Friends.Person{
__meta__: #Ecto.Schema.Metadata<:loaded, "people">,
age: 28,
first_name: "im",
id: 1,
last_name: "miolab"
}

iex(3)> changeset = Friends.Person.changeset(person, %{age: 18})
#Ecto.Changeset<
action: nil,
changes: %{age: 18},
errors: [],
data: #Friends.Person<>,
valid?: true
>

iex(4)> Friends.Repo.update(changeset)

13:10:28.235 [debug] QUERY OK db=1.8ms queue=2.5ms idle=1835.9ms
UPDATE "people" SET "age" = $1 WHERE "id" = $2 [18, 1]
{:ok,
%Friends.Person{
__meta__: #Ecto.Schema.Metadata<:loaded, "people">,
age: 18,
first_name: "im",
id: 1,
last_name: "miolab"
}}
```

#### 結果確認(テーブル)

```postgres
friends_repo=# select * from people;
id | first_name | last_name | age
----+------------+-----------+-----
2 | foo | miolab | 27
3 | foobar | hogehoge | 26
4 | eli | xir | 26
1 | im | miolab | 18
(4 rows)
```

---

## レコード削除(Delete)

- `delete()`

`$ iex -S mix` で確認。

- iex

`id: 3` のレコードを削除します。

```elixir
iex(5)> person_delete = Friends.Repo.get(Friends.Person, 3)

13:44:06.427 [debug] QUERY OK source="people" db=0.8ms idle=1014.5ms
SELECT p0."id", p0."first_name", p0."last_name", p0."age" FROM "people" AS p0 WHERE (p0."id" = $1) [3]
%Friends.Person{
__meta__: #Ecto.Schema.Metadata<:loaded, "people">,
age: 26,
first_name: "foobar",
id: 3,
last_name: "hogehoge"
}

iex(6)> Friends.Repo.delete(person_delete)

13:44:47.048 [debug] QUERY OK db=2.4ms queue=2.1ms idle=1632.1ms
DELETE FROM "people" WHERE "id" = $1 [3]
{:ok,
%Friends.Person{
__meta__: #Ecto.Schema.Metadata<:deleted, "people">,
age: 26,
first_name: "foobar",
id: 3,
last_name: "hogehoge"
}}
```

#### 結果確認(テーブル)

- iex

```
iex(7)> Friends.Person |> Friends.Repo.all

13:45:27.760 [debug] QUERY OK source="people" db=0.5ms queue=0.9ms idle=1347.4ms
SELECT p0."id", p0."first_name", p0."last_name", p0."age" FROM "people" AS p0 []
[
%Friends.Person{
__meta__: #Ecto.Schema.Metadata<:loaded, "people">,
age: 27,
first_name: "foo",
id: 2,
last_name: "miolab"
},
%Friends.Person{
__meta__: #Ecto.Schema.Metadata<:loaded, "people">,
age: 26,
first_name: "eli",
id: 4,
last_name: "xir"
},
%Friends.Person{
__meta__: #Ecto.Schema.Metadata<:loaded, "people">,
age: 18,
first_name: "im",
id: 1,
last_name: "miolab"
}
]
```

`id: 3` のレコードが削除されていることを確認できました。

---