Sounds like you’re trying to build everything in one go? But since the playlist doesn’t exist yet, it doesn’t have an id
, so it can it be given to the videos, so they fail their validation.
Confession: I can’t remember the last time I used cast_assoc
personally, I set association ids manually. I also don’t like how IIRC you have to always give all the relationships in total which can be cumbersome.
I would do something like this, where its a two step transaction. You can also try ecto.multi.
###
### Writing this on my way out the door, so take it as a suggestion
###
def create_playlist_with_videos(name, category, videos) do
# I normally have my own wrapper around transaction, so trying to remember
# exactly without docs.
Repo.transaction(fn ->
with {:ok, playlist} <- create_playlist(name, category),
# now playlist exists with an id so we can create videso
{:ok, videos} <- create_playlist_videos(playlist, videos) do
# transaction() wraps result in {:ok, _}
%{playlist | videos: videos}
else
# can write this nicer, ideally the {:error, _} could just fall
# out and fail the transaction
{:error, e} -> rollback(e)
end
end)
end
defp create_playlist(name, category) do
%Playlist{}
# its ok to have more than one changeset function, i find it useful to
# have at least a create_ and update_ as often they want different
# validations.
|> Playlist.create_changeset(name, category)
|> Repo.insert()
end
defp create_playlist_videos(%Playlist{} = p, videos) do
videos
|> Enum.map(fn v ->
%Video{}
|>Video.create_changeset(playlist.id, v.name, v.url)
|> Repo.insert()
end)
|> Enum.reduce({:ok, []}, fn
# acc is error, perpetuate
# you can use reduce_while to quit out
_, {:error, e} -> {:error, e}
# vid was ok, save and keep going
{:ok, vid}, {:ok, vids} -> {:ok, [vids | vid]}
# vid was error, return error (or collate all errors or ...?)
# how you handle these errors is kind of application specific
{:error, cs} _ -> {:error, :idk_lol}
end)
end
e: you also probably want to put a null: false
constraint in the database too if you haven’t.