スポンサーリンク

2016年8月11日

[Elixir]夏コミ用の内容を一部公開(第二弾)

Goal

  • 2016年度の夏コミ用の内容を一部公開

Dev-Environment

OS: Windows8.1
Erlang/OTP: v19.0 (EShell v8)
Elixir: v1.3.0
Phoenix: v1.2

Context

夏コミ用に執筆している本の一部を先行して公開します。
公開している内容は、Ectoの外部キーについてです。
問題があったら消すかもしれません。購入の参考になったら幸いです。

外部キーの作成

マイグレーションから外部キーを作成してみましょう。
外部キーを作るには、Ecto.Migration.references/2を使います。
新しく:tasksテーブルを追加し、:usersテーブルへの外部キーを作成します。
マイグレーションファイルの内容は下記のようになります。

Example:

defmodule Ecto2Example.Repo.Migrations.CreateTask do
  use Ecto.Migration

  def change do
    create table(:tasks) do
      add :name, :string
      add :user_id, references(:users, on_delete: :nothing)

      timestamps()
    end
    create index(:tasks, [:user_id])

  end
end
外部キーを指定するには、referencesを利用します。
簡単にですが、オプションについて説明をしたいと思います。
  • :name
外部キー名の指定ができます。
外部キー名は、デフォルトだと”[table]_[column]_fkey”と言う名前になります。
今回の場合ですと、”posts_user_id_fkey”ですね。
  • :column
外部キー列の指定ができます。デフォルトでは:idになっています。
上記の例だと:usersの:idになりますね。これを:usersの:nameに変えたいとなったときに指定するオプションです。
  • :type
外部キーのタイプを指定することができます。デフォルトでは、:serialになっています。
  • :on_delete
エントリが削除された場合、どうするかの設定が行えます。
設定には、何もしない(:nothing)、全て削除(:delete_all)、全て無効(:nilify_all)があります。
デフォルトでは、:nothingになっています。
  • :on_update
:on_deleteオプションとほぼ同じです。
エントリが更新された場合、どうするかの設定が行えます。
設定には、何もしない(:nothing)、全て更新(:update_all)、全て無効(:nilify_all)があります。
デフォルトでは、:nothingになっています。

Note:

:on_delete、:on_updateオプションについてもう少し詳しく・・・

削除か更新かでしかほぼ変わらんので、:on_deleteを例に取って説明します。
例えば、:usersテーブルと:tasksテーブルが1対多の関係に設定するとします。
:tasksのマイグレーションファイルの外部キーで:on_deleteオプションを:delete_allに設定していると、
:usersの行データが消されたとき、:tasksの関係しているデータも消してくれるということです。

別段、トランザクションの一連の流れとして先に削除してもいいですし、
自分で:tasksのデータを消した後、:usersのデータを削除してもいいです。
まぁ、これを設定していると手間が省けるということですね。

ただ、プログラム上ではRepo.delete/2でユーザのデータを消しているだけに見えますから、
情報を共有していないと関連するデータの削除を別の人がプログラム上で行ってしまうことがあるかもしれません。

Example:

> ecto.migrate
データベースへ接続し、:users、:tasksテーブルを確認してみます。

Example:

                                      Table "public.users"
   Column    |            Type             |                     Modifiers
-------------+-----------------------------+----------------------------------------------------
 id          | integer                     | not null default nextval('users_id_seq'::regclass)
 name        | character varying(255)      |
 email       | character varying(255)      |
 inserted_at | timestamp without time zone | not null
 updated_at  | timestamp without time zone | not null
Indexes:
    "users_pkey" PRIMARY KEY, btree (id)
Referenced by:
    TABLE "tasks" CONSTRAINT "tasks_user_id_fkey" FOREIGN KEY (user_id) REFERENCES users(id)
:tasksへの参照が追加されています。

Example:

                                      Table "public.tasks"
   Column    |            Type             |                     Modifiers
-------------+-----------------------------+----------------------------------------------------
 id          | integer                     | not null default nextval('tasks_id_seq'::regclass)
 name        | character varying(255)      |
 user_id     | integer                     |
 inserted_at | timestamp without time zone | not null
 updated_at  | timestamp without time zone | not null
Indexes:
    "tasks_pkey" PRIMARY KEY, btree (id)
    "tasks_user_id_index" btree (user_id)
Foreign-key constraints:
    "tasks_user_id_fkey" FOREIGN KEY (user_id) REFERENCES users(id)
インデックスと外部キーが設定されていますね。

Note:

Phoenixの生成コマンドからモデルまで含めて一括で外部キーを作ることができます。

> mix phoenix.gen.model Task tasks name:string user_id:references:users

上記のコマンドで生成されるモデルファイルは下記のものになります。
また、生成されるマイグレーションファイルは上記の方で掲載しているものと同じになります。

defmodule Ecto2Example.Task do
  use Ecto2Example.Web, :model

  schema "tasks" do
    field :name, :string
    belongs_to :user, Ecto2Example.User

    timestamps()
  end

  @doc """
  Builds a changeset based on the `struct` and `params`.
  """
  def changeset(struct, params \\ %{}) do
    struct
    |> cast(params, [:name])
    |> validate_required([:name])
  end
end

belongs_toが追加されています。便利ですね。
たとえ一行であっても自分で書くの減らしたいと言う方のためにあるのでしょう。(嘘です)

Bibliography

なし

人気の投稿