Hi,
I am new to both Elixir and Phoenix and to learn I have been building a simple App similar to what I have in production in Python. I am stuck on trying to create an Account record when a User is created (on the basis that every user must have an account, but a user could belong to many accounts, and an account could be shared with multiple users). I am using POW for auth and that was working fine until I tried to extend what was happening to automatically create an account when creating a user. This is what I have:
defmodule TestApp.Accounts.Account do
@moduledoc """
Main high level account that users belong to.
"""
require Logger;
use Ecto.Schema
import Ecto.Changeset
alias TestApp.Accounts.{User, UserAccount}
schema "accounts" do
field :name, :string
field :suspended, :boolean, default: false
field :owner, :id
field :company_name, :string
field :address, :string
field :city, :string
field :county, :string
field :country, :string
field :zip_or_postcode, :string
many_to_many :users, User, join_through: UserAccount
timestamps()
end
@doc false
def changeset(account, attrs) do
account
|> Map.put(:name, attrs[:email])
|> cast(attrs, [:name])
end
end
defmodule TestApp.Accounts.User do
use Ecto.Schema
use Pow.Ecto.Schema
import Ecto.Changeset
alias TestApp.Accounts.{Account, UserAccount}
schema "users" do
pow_user_fields()
field :first_name, :string
field :last_name, :string
field :type, :string
field :bio, :string
field :photo, :string
field :provider, :string
field :token, :string
field :suspended, :boolean
field :locked, :boolean
field :units, :string
field :currency, :string
field :company_name, :string
field :address, :string
field :city, :string
field :county, :string
field :country, :string
field :zip_or_postcode, :string
field :terms_signed, :boolean
many_to_many :accounts, Account, join_through: UserAccount
timestamps()
end
@doc false
def changeset(user_or_changeset, attrs) do
user_or_changeset
|> cast(attrs, [:first_name, :last_name, :email, :provider, :token, :password_hash, :bio, :photo])
|> pow_changeset(attrs)
|> validate_required([:email, :password])
|> unique_constraint(:email)
|> maybe_add_account()
end
defp maybe_add_account(changeset) do
case Ecto.get_meta(changeset.data, :state) do
:built -> add_account(changeset)
:loaded -> changeset
end
end
defp add_account(changeset) do
cast_assoc(changeset, :accounts, required: true )
end
end
defmodule TestApp.Accounts.UserAccount do
@moduledoc """
Join throught table for Users and Accounts. So that Users always belong to at least one account.
"""
use Ecto.Schema
import Ecto.Changeset
alias TestApp.Accounts.{Account, User}
schema "user_accounts" do
belongs_to :user, User
belongs_to :account, Account
timestamps()
end
@doc false
def changeset(user_account, attrs) do
user_account
|> cast(attrs, [:user_id, :account_id])
|> validate_required([:user_id, :account_id])
end
end
And Migrations:
defmodule TestApp.Repo.Migrations.CreateUsers do
use Ecto.Migration
def change do
create table(:users) do
add :email, :string, null: false
add :password_hash, :string
add :first_name, :string
add :last_name, :string
add :type, :string
add :bio, :text
add :photo, :string
add :provider, :string
add :token, :string
add :suspended, :boolean, default: false
add :locked, :boolean, default: false
add :units, :string
add :currency, :string
add :company_name, :string
add :address, :string
add :city, :string
add :county, :string
add :country, :string
add :zip_or_postcode, :string
add :terms_signed, :boolean, default: false
timestamps()
end
create unique_index(:users, [:email])
end
end
defmodule TestApp.Repo.Migrations.CreateAccounts do
use Ecto.Migration
def change do
create table(:accounts) do
add :name, :string
add :suspended, :boolean, default: false, null: false
add :company_name, :string
add :address, :string
add :city, :string
add :county, :string
add :country, :string
add :zip_or_postcode, :string
add :owner, references(:users, on_delete: :nothing)
timestamps()
end
create index(:accounts, [:owner])
end
end
defmodule TestApp.Repo.Migrations.CreateUserAccounts do
use Ecto.Migration
def change do
create table(:user_accounts) do
add :user_id, references(:users, on_delete: :nothing)
add :account_id, references(:accounts, on_delete: :nothing)
timestamps()
end
create index(:user_accounts, [:user_id])
create index(:user_accounts, [:account_id])
create unique_index(:user_accounts, [:user_id, :account_id])
end
end
I have an phx generated context for users, but nothing else, and what I think might be relevant is the create_user is just:
def create_user(attrs \\ %{}) do
%User{}
|> User.changeset(attrs)
|> Repo.insert()
end
Because I think the Changeset is what actually is done via ecto to the database? Perhaps I am wrong in that assumption.
Any help would be really appreciated.
Thanks! Tim