I’m trying to insert 2 associated models into the repo using the create_xxx functions generated by “mix ecto.gen.json”. I haven’t found a way to do this without getting various errors and I couldn’t find a post on this anywhere.
I have two models: Menu and MenuItem where MenuItem has a FK to store the associated Menu.
The following works without any issues, both records are created in the database:
menu = %Cms.Content.Menu{name: "Test"}
menu_item = %Cms.Content.MenuItem{name: "Test Item", menu: menu}
Cms.Repo.insert!(menu_item)
But when I try to use the create_xxx functions I get an error:
menu = Cms.Content.create_menu(%{name: "Test Menu"}) # works fine
menu_item = Cms.Content.create_menu_item(%{name: "Test Item", menu: menu}). # error
[debug] QUERY ERROR db=6.5ms
INSERT INTO “menus” (“inserted_at”,“updated_at”) VALUES ($1,$2) RETURNING “id” [{{2018, 2, 28}, {10, 58, 43, 347401}}, {{2018, 2, 28}, {10, 58, 43, 347411}}]
[debug] QUERY OK db=0.3ms
rollback []
** (Postgrex.Error) ERROR 23502 (not_null_violation): null value in column “name” violates not-null constraint
table: menus
column: name
Why does Ecto try to insert the already created menu again? What is wrong here?
Here are the model definitions:
defmodule Cms.Content.Menu do
use Ecto.Schema
import Ecto.Changeset
alias Cms.Content.{Menu, MenuItem}
schema "menus" do
field :name, :string
has_many :menu_items, MenuItem
timestamps()
end
@doc false
def changeset(%Menu{} = menu, attrs) do
menu
|> cast(attrs, [:name])
|> validate_required([:name])
|> unique_constraint(:name, message: "Name is already taken.")
end
end
defmodule Cms.Content.MenuItem do
use Ecto.Schema
import Ecto.Changeset
alias Cms.Content.{Menu, MenuItem}
schema "menu_items" do
field :name, :string
belongs_to :menu, Menu
timestamps()
end
@doc false
def changeset(%MenuItem{} = menu_item, attrs) do
menu_item
|> cast(attrs, [:name])
|> put_assoc(:menu, [attrs.menu])
|> validate_required([:name, :menu])
|> unique_constraint(:name, message: "Name is already taken.")
|> unique_constraint(:order, message: "Order number is already taken.")
end
end