LoginSignup
3
1

More than 5 years have passed since last update.

PhoenixでYamlで定義したシードデータを投入する

Last updated at Posted at 2017-10-01

Phoenixでのシードデータ投入

phoenixでは、

priv/repo/seeds.exs
%App.SomeModel{}
|> App.Repo.insert!

的なelixirコードを記述して mix run priv/repos/seed.exs と実行してやれば、シードデータ投入が出来るみたいです。

mix ecto.resetを実行すると、drop→create→migrate→seed投入、の順番で実行します。開発の初期段階だと便利ですね。

しかし、まぁ投入データをEctoの記法でいちいち記述するのも面倒臭いので、YAMLで書けた方が便利(?)と思ったのでまー試しにやって見ます。

必要なモジュール

mix.exs
{:yaml_elixir, "~> 1.1"},
$ mix deps.get

データベース設定

MySQL 使ってます。

config/dev.exs
config :app, App.Repo,
  adapter: Ecto.Adapters.MySQL,
  username: "root",
  password: "",
  database: "app_dev",
  hostname: "mysql",
  pool_size: 10

モデル

AreaモデルとPrefectureモデルを想定します。
Area : Prefecture = 1 : n の関係にあります。
Prefectureが、Areaのcodeをarea_codeとして持っている形です。

web/models/area.ex
defmodule App.Area do
  use Ecto.Schema
  alias App.Prefecture

  @primary_key {:code, :string, autogenerate: false}
  schema "areas" do
    field :name, :string

    has_many  :prefectures, Prefecture, 
              references: :code,
              foreign_key: :area_code
  end
end
web/models/prefecture.ex
defmodule App.Prefecture do
  use Ecto.Schema
  alias App.Area

  @primary_key {:code, :string, autogenerate: false}
  schema "prefectures" do
    field :name, :string

    belongs_to :area, Area, 
              references: :code,
              foreign_key: :area_code,
              type: :string
  end
end

Fixtureモジュール定義

yamlを読み込んでデータ投入するためのモジュールを作成します。
別のプロジェクトでも使えるようにする為に、App.Repoの関数呼んでるところをもうちょっと抽象化したい感じがします。

web/models/concerns/fixture.ex
defmodule App.Model.Fixture do
  alias App.Model.Fixture

  def create(model) do
    struct(model)
  end

  def create(model, params) do
    struct(model, params)
  end

  def primary_key(model, params) do
    create(model, params)
    |> Ecto.Changeset.cast(params, Map.keys( params ))
    |> Ecto.Changeset.apply_changes
    |> Ecto.primary_key
  end

  def find(model, primary_key) do
    App.Repo.get_by model, primary_key
  end

  def insert(file_name, model) do
    Fixture.get_params_list_from(file_name)
    |> Fixture.insert_list(model)
  end

  def insert_or_update(file_name, model) do
    Fixture.get_params_list_from(file_name)
    |> Fixture.insert_or_update_list(model)
  end

  def get_params_list_from(file_path) do
    File.cwd!
    |> Path.join(file_path)
    |> YamlElixir.read_from_file
  end

  def insert_list(params_list, model) do
    for params <- params_list do
      model.change(params)
      |> App.Repo.insert!  
    end
  end

  def insert_or_update_list(params_list, model) do
    for params <- params_list do
      object = find(model, primary_key(model, params) ) || create(model)
      object
      |> Ecto.Changeset.cast(params, Map.keys( params ))
      |> App.Repo.insert_or_update!
    end
  end
end

定義ファイル

priv/repo/fixtures/areas.yml
- code: '01'
  name: 北海道・東北
- code: '02'
  name: 関東
- code: '03'
  name: 甲信越・北陸
- code: '04'
  name: 東海
- code: '05'
  name: 関西
- code: '06'
  name: 中国
- code: '07'
  name: 四国
- code: '08'
  name: 九州・沖縄
priv/repo/fixtures/prefectures.yml
- code: '01'
  name: '北海道'
  area_code: '01'
- code: '02'
  name: '青森県'
  area_code: '01'
- code: '03'
  name: '岩手県'
  area_code: '01'
- code: '04'
  name: '宮城県'
  area_code: '01'
- code: '05'
  name: '秋田県'
  area_code: '01'
- code: '06'
  name: '山形県'
  area_code: '01'
- code: '07'
  name: '福島県'
  area_code: '01'
- code: '08'
  name: '茨城県'
  area_code: '02'
- code: '09'
  name: '栃木県'
  area_code: '02'
- code: '10'
  name: '群馬県'
  area_code: '02'
- code: '11'
  name: '埼玉県'
  area_code: '02'
- code: '12'
  name: '千葉県'
  area_code: '02'
- code: '13'
  name: '東京都'
  area_code: '02'
- code: '14'
  name: '神奈川県'
  area_code: '02'
- ...
- ...
-

実行ファイル

priv/repo/seeds.exs
alias App.Model.Fixture

Fixture.insert_or_update("priv/repo/fixtures/areas.yml", App.Area)
Fixture.insert_or_update("priv/repo/fixtures/prefectures.yml", App.Prefecture)

いちいち指定するのが面倒であれば、priv/repo/fixtures以下のymlを全部見るようにしても良さそう。
Fixtureの関数として追加してもいい気がする。

実行

$ mix run priv/repo/seeds.exs

Ruby/Railsは4〜5年使っていますが、Elixir/Phoenixは始めて10日程度の素人です。
もうちょっといい方法や書き方とかがあったら教えていただけると幸いです。

3
1
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
3
1