tnederlof

tnederlof

Issue Seeding With Ecto

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!

Most Liked

kokolegorille

kokolegorille

It is usually not a good idea to expose id field in the changeset.

You can do it in different ways, but if You have already a user, You could use put_assoc.

Another way to do it is to use build_assoc.

Last Post!

tnederlof

tnederlof

Thank you! Reading through put_assoc I learned a lot more about associations and changesets. I can see why exposing the id field to the changeset is a bad idea, I will stick with using put and build for now. I appreciate your help again and patience, I am learning a lot!

Where Next?

Popular in Questions Top

RisingFromAshes
I’ve read in another post that it may be possible with a router helper - but I couldn’t find an appropriate one, and tbh, I’m still just ...
New
baxterw3b
Hi guys, i’m new in the Elixir world, and i have to say, that i love it! i’m having some problem to understand anonymous functions with ...
New
vonH
In asking this question I am more interested about the expressiveness of the language itself and less concerned about the availability of...
New
Brian
What is the proper way to load a module from a file in to IEX? In the python world, doing something like this pretty standard: from ....
New
bsollish-terakeet
Credo is smart enough to check for (something like) this: assert length(the_list) == 0 with this response: Checking if an enum is empt...
New
dblack
I’ve got an issue with an app and I’ve no idea of how to troubleshoot it. I’m hoping someone here might have seen something similar. I p...
New
Patoshizzle
After calling mix ecto.create I get this error: 17:00:32.162 [error] GenServer #PID&lt;0.412.0&gt; terminating ** (Postgrex.Error) FATAL...
New

Other popular topics Top

KronicDeth
Elixir plugin for JetBrain’s IntelliJ Platform (including Rubymine) This is a plugin that adds support for Elixir to JetBrains IntelliJ...
289 36689 110
New
JeremM34
Hello, how can I check the Phoenix version ? Thanks !
New
baxterw3b
Hi guys, i’m new in the Elixir world, and i have to say, that i love it! i’m having some problem to understand anonymous functions with ...
New
bsollish-terakeet
Credo is smart enough to check for (something like) this: assert length(the_list) == 0 with this response: Checking if an enum is empt...
New
WestKeys
Currently suffering from paralysis by [HTTP client] analysis. This is rather unusual in Elixirland as there tends to be consensus on the ...
New
TunkShif
This post is an instruction guide to help you setup your Neovim for Elixir development from scratch. It includes general information on h...
274 42576 114
New

We're in Beta

About us Mission Statement