12
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

[Phoenix]フィールドのvirtualオプションを検証する

Last updated at Posted at 2015-07-02

#目的
Ectoのフィールドに付加するvirtualオプションを把握する。

#実行環境
OS: Windows8.1
Erlang: Eshell V6.4, OTP-Version 17.5
Elixir: v1.0.4
Phoenix Framework: v0.13.1
PostgreSQL: postgres (PostgreSQL) 9.4.4

#始める前に
Railsのパスワード属性みたいなものがないかと思い、
情報を探していたら・・・なんと情報が見つかった。

実際にどんな動作をするのか検証してみました。
プロジェクトの作成やマイグレーションは各自でお願いします。

データモデルは以下を想定・・・
モデル名:User
テーブル名:users
カラム: name:string、email:string、password:string、password_digest:string

#目次

  1. virtualオプションって何?
  2. 検証してみよう!

##1. virtualオプションって何?
スキーマに定義するフィールドのオプションです。
Ectoのドキュメントには以下のように書かれています。

引用:

:virtual - When true, the field is not persisted to the database.
Notice virtual fields do not support :autogenerate nor :read_after_writes.

※nor = or(?)

翻訳:
:virtual - フィールドが正しいとき、データベースまで持続しません。
仮想フィールドがサポートしないのは、:autogenerateまたは:read_after_writesです

つまり、フィールド(値)でエラーが出なければデータベースまで保存しませんよ。
っと言うことですね。

これを何に使うのかと疑問に思っている方もいると思います。
私が想定している内容ですが、あるデータモデルに
password_digest、password、password_confirmationというフィールドがあったとします。

password、password_confirmationは入力と検証は必須にしたいですが、
DBへ格納するデータは暗号化したパスワードが入るpassword_digestだけあれば良いのです。
そこでVirtualオプションを使うわけですね。

##2. 検証してみよう!
では、実際に検証してみます。

Userモデルを修正します。
web/models/user.exを開き、以下のように編集して下さい。

defmodule EctoModelsSample.User do
  use EctoModelsSample.Web, :model
  use Ecto.Model.Callbacks

  before_insert :set_password_digest

  schema "users" do
    field :name, :string
    field :email, :string
    field :password_digest, :string
    field :password, :string, virtual: true

    timestamps
  end

  @required_fields ~w(name email password)
  @optional_fields ~w()

  @doc """
  Creates a changeset based on the `model` and `params`.

  If `params` are nil, an invalid changeset is returned
  with no validation performed.
  """
  def changeset(model, params \\ :empty) do
    model
    |> cast(params, @required_fields, @optional_fields)
    |> validate_length(:password, min: 8)
    |> validate_length(:password, max: 100)
  end

  def set_password_digest(changeset) do
    password = Ecto.Changeset.get_field(changeset, :password)
    change(changeset, %{password_digest: password})
  end
end

※ 注意1
本当はset_password_digest/1の関数内で
暗号化しないといけないのですが、今回は割愛して生のまま入れています。
(ライブラリを入れるのが面倒くさかったとかそんな理由ではありません!)

※ 注意2
上記のソースコードだと、
password_digestを表示させようとすると実行時エラーで落ちます。
多分、@require_fieldに入ってないからだと思います。(未検証)

画面で確認したい方はテンプレートも修正して下さい。
:passwordを入力&表示できるようにします。

対象テンプレートは以下。
web/templates/user/index.html.eex
web/templates/user/show.html.eex
web/templates/user/form.html.eex

※indexかshowのどちらかだけでも確認はできます。(あくまで一例と言うことで・・・)

index.html.eex

<h2>Listing users</h2>

<table class="table">
  <thead>
    <tr>
      <th>Name</th>
      <th>Email</th>
      <th>Password</th>

      <th></th>
    </tr>
  </thead>
  <tbody>
<%= for user <- @users do %>
    <tr>
      <td><%= user.name %></td>
      <td><%= user.email %></td>
      <td><%= user.password %></td>

      <td class="text-right">
        <%= link "Show", to: user_path(@conn, :show, user), class: "btn btn-default btn-xs" %>
        <%= link "Edit", to: user_path(@conn, :edit, user), class: "btn btn-default btn-xs" %>
        <%= link "Delete", to: user_path(@conn, :delete, user), method: :delete, class: "btn btn-danger btn-xs" %>
      </td>
    </tr>
<% end %>
  </tbody>
</table>

<%= link "New user", to: user_path(@conn, :new) %>

show.html.eex

<h2>Show user</h2>

<ul>

  <li>
    <strong>Name:</strong>
    <%= @user.name %>
  </li>

  <li>
    <strong>Email:</strong>
    <%= @user.email %>
  </li>

  <li>
    <strong>Password:</strong>
    <%= @user.password %>
  </li>

</ul>

<%= link "Back", to: user_path(@conn, :index) %>

form.html.eex

<%= form_for @changeset, @action, fn f -> %>
  <%= if f.errors != [] do %>
    <div class="alert alert-danger">
      <p>Oops, something went wrong! Please check the errors below:</p>
      <ul>
        <%= for {attr, message} <- f.errors do %>
          <li><%= humanize(attr) %> <%= message %></li>
        <% end %>
      </ul>
    </div>
  <% end %>

  <div class="form-group">
    <label>Name</label>
    <%= text_input f, :name, class: "form-control" %>
  </div>

  <div class="form-group">
    <label>Email</label>
    <%= text_input f, :email, class: "form-control" %>
  </div>

  <div class="form-group">
    <label>Password</label>
    <%= text_input f, :password, class: "form-control" %>
  </div>

  <div class="form-group">
    <%= submit "Submit", class: "btn btn-primary" %>
  </div>
<% end %>

後は、普通に画面からユーザを作成してみれば・・・あら不思議!
DBを確認すると、password_digestには値が入っていますが、passwordの値は空です。

#管理人の独り言~
今回はピンポイントで非常に参考になる情報が見つかった。
いや~情報が見つからなかったら、ここら辺飛ばしてたから良かった良かった。

#参考文献
Rewriting elixirstream.com from Rails to Phoenix
hexdocs - Ecto.Schema

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?