Ecto: Save nested records example
To start this tut let me introduce the Parent and Child schemas, engaged in a standard one-to-many relationship:
defmodule MyApp.Parent do
use Ecto.Schema
schema "parents" do
field :name, :string
has_many :children, MyApp.Child
end
end
defmodule MyApp.Child do
use Ecto.Schema
schema "children" do
field :name, :string
belongs_to :parent, MyApp.Parent
end
end
The crux lies in the changesets. Here are the structured changesets for both the parent and child records:
defmodule MyApp.Parent do
# ...
def changeset(parent, attrs \\ %{}) do
parent
|> cast(attrs, [:name])
|> validate_required([:name])
|> cast_assoc(:children, with: &MyApp.Child.changeset/2)
end
end
defmodule MyApp.Child do
# ...
def changeset(child, attrs \\ %{}) do
child
|> cast(attrs, [:name])
|> validate_required([:name])
end
end
Now, let’s navigate the process of creating and inserting records:
parent_attrs = %{name: "Parent Name", children: [%{name: "Child 1"}, %{name: "Child 2"}]}
changeset = MyApp.Parent.changeset(%MyApp.Parent{}, parent_attrs)
case MyApp.Repo.insert(changeset) do
{:ok, parent} ->
# Insertion successful
{:error, changeset} ->
# Handle errors
end
This example assumes the creation of a new parent with associated child records.
For those inclined towards updating existing records, the process is straightforward. Retrieve the parent, apply the updated attributes, and execute the update:
parent = MyApp.Repo.get!(MyApp.Parent, parent_id)
updated_attrs = %{name: "Updated Parent Name", children: [%{id: 1, name: "Updated Child 1"}, %{name: "New Child"}]}
changeset = MyApp.Parent.changeset(parent, updated_attrs)
case MyApp.Repo.update(changeset) do
{:ok, updated_parent} ->
# Update successful
{:error, changeset} ->
# Handle errors
end
Ensure to provide the id for existing child records for the update to succeed.
I hope this helped! Have a good one!
Check out another post: ELIXIR ENUM.MAP/2 VS RUBY ARRAY#MAP