I have a Phoenix app which is using ecto/postgres. I have setup two schemas (User has many Groups) and a seed file (contents below). I am running into an error when I add the group (which is supposed to have a user). I feel like the user association, where the constraint goes (requiring user_id vs assoc_constraint(:user)) and then how the seeding flows I am confused on.
Error I am getting
** (Ecto.InvalidChangesetError) could not perform insert because changeset is invalid.
Errors
%{user_id: [{"can't be blank", [validation: :required]}]}
Applied changes
%{description: "Random text", name: "Family"}
Params
%{
"description" => "Random text",
"name" => "Family",
"user" => %XXXX.Accounts.User{
__meta__: #Ecto.Schema.Metadata<:loaded, "users">,
email: "example@gmail.com",
groups: #Ecto.Association.NotLoaded<association :groups is not loaded>,
id: 1,
inserted_at: ~N[2020-05-19 22:43:11],
name: "Example User",
password: "secret",
password_hash: "XXXXXXXX",
phonenumber: "1 203-247-7113",
updated_at: ~N[2020-05-19 22:43:11],
username: "exampleuser"
}
}
Changeset
#Ecto.Changeset<
action: :insert,
changes: %{description: "Random text", name: "Family"},
errors: [user_id: {"can't be blank", [validation: :required]}],
data: #XXXX.People.Group<>,
valid?: false
User Schema
defmodule XXXX.Accounts.User do
use Ecto.Schema
import Ecto.Changeset
schema "users" do
field :email, :string
field :name, :string
field :password_hash, :string
field :password, :string, virtual: true
field :phonenumber, :string
field :username, :string
has_many :groups, XXXX.People.Group
timestamps()
end
def changeset(user, attrs) do
user
|> cast(attrs, [:name, :username, :email, :password, :phonenumber])
|> validate_required([:name, :username, :email, :password, :phonenumber])
|> validate_length(:username, min: 4)
|> validate_length(:password, min: 4)
|> unique_constraint(:username)
|> unique_constraint(:email)
|> unique_constraint(:phonenumber)
|> hash_password()
end
defp hash_password(changeset) do
case changeset do
%Ecto.Changeset{valid?: true, changes: %{password: password}} ->
put_change(changeset, :password_hash, Pbkdf2.hash_pwd_salt(password))
_ ->
changeset
end
end
end
User Migration
defmodule XXXX.Repo.Migrations.CreateUsers do
use Ecto.Migration
def change do
create table(:users) do
add :name, :string, null: false
add :username, :string, null: false
add :email, :string, null: false
add :password_hash, :string, null: false
add :phonenumber, :string, null: false
timestamps()
end
create unique_index(:users, [:username, :email, :phonenumber])
end
end
Group Schema
defmodule XXXX.People.Group do
use Ecto.Schema
import Ecto.Changeset
schema "groups" do
field :description, :string
field :name, :string
belongs_to :user, XXXX.Accounts.User
timestamps()
end
def changeset(group, attrs) do
required_fields = [:name, :user_id]
optional_fields = [:description]
group
|> cast(attrs, required_fields ++ optional_fields)
|> validate_required(required_fields)
|> assoc_constraint(:user)
end
end
Group Migration
defmodule XXXX.Repo.Migrations.CreateGroups do
use Ecto.Migration
def change do
create table(:groups) do
add :name, :string, null: false
add :description, :text
add :user_id, references(:users, on_delete: :delete_all), null: false
timestamps()
end
end
end
seeds.exs
alias XXXX.Repo
alias XXXX.People.Group
alias XXXX.Accounts.User
user_example_1 = %User{} |> User.changeset(%{
name: "Example User",
username: "exampleuser",
phonenumber: "1 834-000-0000",
email: "example@gmail.com",
password: "secret"
}) |> Repo.insert!
example_group1 = %Group{} |> Group.changeset(%{
name: "Family",
description: "Random text",
user: user_example_1
}) |> Repo.insert!