has_one や belongs_to の関係になっているDeviceとUserのモデルを考えた時に、Device作成時にUserモデルも新規に作成したい時の話。
このメソッドは対象は、Phoenixのcreateリクエストを元にしています。
Rep.transaction とその中で依存性を持つ複数のDB処理を一括で書いています。
has_one や belongs_to の関係を持っているので、一方の _id に、もう一方の id を与える必要がある、ところがポイント。
もっとちゃんとした書き方あるのかな。ありそうな気がする。。。
def create(conn, %{"device" => device_params}) do
changeset = Device.changeset(%Device{}, device_params)
if changeset.valid? do
# 以下、追加しているところ
Repo.transaction(fn->
# deviceをまず作成
device = Repo.insert!(changeset)
# device.idとして belongs_to の関係を持つ User を作成
user = Repo.insert! %User{device_id: device.id, user_name: "your name", user_sex: "male"}
# has_oneの関係をdeviceは持つので、userとして直前で生成した
# userを与える
device = Repo.update! %Device{device | user: user}
end)
conn
|> put_flash(:info, "Device created successfully.")
|> redirect(to: device_path(conn, :index))
else
render(conn, "new.html", changeset: changeset)
end
end
この関係が正しくできているかは、controllerレベルでは以下のように適当なユーザを生成して、その上でindexなどが表示できることを確認すればOK。
この時、index.htmlでは device.user.user_name のような、has_oneの関係にある要素を描画しようとしていることが必要ですが。
test "lists all entries on index after create items.", %{conn: conn} do
post conn, device_path(conn, :create), device: @valid_attrs
conn = get conn, device_path(conn, :index)
assert html_response(conn, 200) =~ "Listing devices"
end
Railsに似ているので、Railsも参考にしながら書いてみているけれど、DB周りの扱いがだいぶ想像できるようになってきた感じ。
まだ公開していないけれど、練習用repositoryはこちら