6
3

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で既存のDjango製DB(Postgresql)に接続する

Posted at

この記事は、Elixir or Phoenix Advent Calendar 2018の20日目の記事です。
昨日の記事は@ma2geさんの「Elixir School の日本語訳更新方法」でした。

はじめに

既存アプリケーションのAPIとしてPhoenixを採用しました。既存アプリケーションはDjangoで動いているので、言語としてもフレームワークとしても一部の思想がかなり異なり、APIを開発していく中で多少の苦労があったのですが、ここではそのファーストステップであったDjangoのORマッパーで作成したPostgresqlのDBに接続するまでの記録を記事にしています。

Schemaを作成する

ここでSchema(Model)を定義し、migrationファイルの生成を行います。ref:mix phx.gen.schema

❯ mix phx.gen.schema User users password:string last_login:naive_datetime is_superuser:boolean username:string first_name:string last_name:string email:string is_staff:boolean is_active:boolean date_joined:naive_datetime avatar:string --no-schema --table custom_user
* creating lib/myapp/user.ex
* creating priv/repo/migrations/20181206121950_create_custom_user.exs

Remember to update your repository by running migrations:

    $ mix ecto.migrate
  • UserがSchema名、usersがDBのテーブル名となっています。上述の公式docを見るとわかるようにPhoenixのDDDの思想に従えばAccount.Useraccounts_userなどとしてcontextのもとに配置するのがそのwayにのっているでしょうが、ここでは既存のアプリケーションの設計に従います。
  • password:stringなどでカラム名とデータ型を定義しているのですが、date_joined:naive_datetimeのようにPostgresqlのtimestamptz型はEctoの:naive_datetime型(ElixirのNaiveDateTime型)に対応させています。ref:Ecto.Schema
  • --no-schemaオプションで既存DBにすでに定義されているカラムをmigrationファイルとして生成しないようにします。
  • --tableオプションでテーブル名を指定しています。 ※Djangoでデフォルトで生成されるユーザーのテーブルはauth_userだったりしますが、それを拡張してcustom_userというテーブルを生成して実装していたのでこのテーブルを指定しています。
  • これでSchemaの定義ファイルとmigrationファイルが生成されました。

migrationファイルなど確認し、migrateします。

❯ mix ecto.migrate

確認

❯ iex -S mix
Erlang/OTP 21 [erts-10.0.8] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:1] [hipe] [dtrace]

Interactive Elixir (1.7.3) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> Myapp.Repo.get! Myapp.User, 1               
[debug] QUERY OK source="custom_user" db=15.7ms decode=4.1ms
SELECT c0."id", c0."avatar", c0."date_joined", c0."email", c0."first_name", c0."is_active", c0."is_staff", c0."is_superuser", c0."last_login", c0."last_name", c0."password", c0."username" FROM "custom_user" AS c0 WHERE (c0."id" = $1) [1]
%Myapp.User{
  __meta__: #Ecto.Schema.Metadata<:loaded, "custom_user">,
  avatar: "",
  date_joined: ~N[2018-12-03 15:46:08.436955],
  email: "admin@example.com",
  first_name: "",
  id: 1,
  is_active: true,
  is_staff: true,
  is_superuser: true,
  last_login: ~N[2018-12-03 15:46:27.053081],
  last_name: "",
  password: "***",
  username: "admin"
}
#>取れた😑

* ついでに子モデルへアクセス
iex(2)> user = Myapp.Repo.get! Myapp.User, 1       
[debug] QUERY OK source="custom_user" db=20.4ms queue=0.1ms
SELECT c0."id", c0."avatar", c0."date_joined", c0."email", c0."first_name", c0."is_active", c0."is_staff", c0."is_superuser", c0."last_login", c0."last_name", c0."password", c0."username" FROM "custom_user" AS c0 WHERE (c0."id" = $1) [1]
%Myapp.User{
  __meta__: #Ecto.Schema.Metadata<:loaded, "custom_user">,
  avatar: "",
  date_joined: ~N[2018-12-03 15:46:08.436955],
  email: "admin@example.com",
  first_name: "",
  hourly_wage: 0,
  id: 1,
  is_active: true,
  is_staff: true,
  is_superuser: true,
  last_login: ~N[2018-12-03 15:46:27.053081],
  last_name: "",
  password: "***",
  shifts: #Ecto.Association.NotLoaded<association :shifts is not loaded>,
  username: "admin"
}
#>userに代入して..

iex(3)> user.shifts
#Ecto.Association.NotLoaded<association :shifts is not loaded>
#>おっ、プリロードする必要があるらしい。

iex(4)> Myapp.Repo.all( Myapp.User) |> Myapp.Repo.preload(:shifts)
[debug] QUERY OK source="custom_user" db=20.8ms decode=0.2ms queue=0.2ms
SELECT c0."id", c0."avatar", c0."date_joined", c0."email", c0."first_name", c0."is_active", c0."is_staff", c0."is_superuser", c0."last_login", c0."last_name", c0."password", c0."username" FROM "custom_user" AS c0 []
[debug] QUERY OK source="shift" db=36.0ms
SELECT s0."id", s0."date", s0."opening_time", s0."closing_time", s0."user_id", s0."user_id" FROM "shift" AS s0 WHERE (s0."user_id" = $1) ORDER BY s0."user_id" [1]
[
  %Myapp.User{
    __meta__: #Ecto.Schema.Metadata<:loaded, "custom_user">,
    avatar: "",
    date_joined: ~N[2018-12-03 15:46:08.436955],
    email: "admin@example.com",
    first_name: "",
    id: 1,
    is_active: true,
    is_staff: true,
    is_superuser: true,
    last_login: ~N[2018-12-03 15:46:27.053081],
    last_name: "",
    password: "***",
    shifts: [
      %Myapp.Shift{
        __meta__: #Ecto.Schema.Metadata<:loaded, "shift">,
        closing_time: ~T[19:00:00.000000],
        date: ~D[2018-12-09],
        id: 1,
        opening_time: ~T[11:00:00.000000],
        user: #Ecto.Association.NotLoaded<association :user is not loaded>,
        user_id: 1
      }
    ],
    username: "admin"
  }
]
#>pipe operator💪

まとめ

Ectoを介してDjango+Postgresqlの既存DBに接続することができました。API開発によって広がる可能性は大きいです。高級言語ながら並行処理・並列処理の扱いに長けたelixirとその長所を見事に活かすフレームワークPhoenix、きっと皆さんの開発の中でも適切な役割を担えると思います。

明日の担当は@kobatakoさんです。

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?