Currently I’m doing it by
defmodule User do
...
schema "users" do
has_one :credentials, Credential
end
def changeset(%User{} = user, attrs) do
user
|> cast(attrs, [])
|> validate_required([])
end
end
defmodule Credential do
...
schema "credentials" do
belongs_to :user, User
end
def changeset(%Credential{} = credential, attrs) do
credential
|> cast(attrs, [:user_id])
|> validate_required([:user_id])
|> assoc_constraint(:user)
end
end
def create_user(attrs) do
credential_attrs = attrs["credential"]
Ecto.Multi.new
|> Ecto.Multi.run(:user, fn _ -> create_user(attrs) end)
|> Ecto.Multi.run(:credential, fn %{user: user} -> create_credential(%{credential_attrs | user_id: user.id}) end)
|> Repo.transaction()
|> case do
{:ok, %{user: user}} -> {:ok, user}
{:error, _falied_step, %Ecto.Changeset{} = changeset, _} -> {:error, changeset}
end
end
But I want to use cast_assoc
like below.
defmodule User do
...
schema "users" do
has_one :credentials, Credential
end
def changeset(%User{} = user, attrs) do
user
|> cast(attrs, [])
|> validate_required([])
|> cast_assoc(:credential, required: true)
end
end
defmodule Credential do
...
schema "credentials" do
belongs_to :user, User
end
def changeset(%Credential{} = credential, attrs) do
credential
|> cast(attrs, [:user_id])
|> validate_required([:user_id])
|> assoc_constraint(:user)
end
end
def create_user(attrs) do
%User{}
|> User.changeset(attrs)
|> Repo.insert()
end
But it’s not possible because put_assoc
creates associated record first then creates self at the end.
Is there any better way to create this kinds of related records?