问题
I'm building an app in Elixir on top of the Phoenix Framework that has a piece of functionality around collecting data from the Spotify API and persisting it into a PostgreSQL 9.4 database for quick access. I'm having trouble with updating existing table rows that use embeds_many
.
I have Track and AlbumArt Ecto models that looks like this:
defmodule CoolApp.Track do
use CoolApp.Web, :model
alias CoolApp.AlbumArt
alias CoolApp.Repo
alias CoolApp.Track
schema "tracks" do
field :sid, :string
field :title, :string
field :artist, :string
field :artist_sid, :string
field :album, :string
field :album_sid, :string
field :sort_order, :integer
field :is_now_playing, :boolean
# embeds_many :album_art, AlbumArt
# added on_replace: :delete
embeds_many :album_art, AlbumArt, on_replace: :delete
timestamps
end
end
defmodule CoolApp.AlbumArt do
use CoolApp.Web, :model
embedded_schema do
field :width, :integer
field :height, :integer
field :url, :string
end
end
The *_sid
fields are the spotify IDs. The embedded album_art
column is an array of data like:
{
"height": 600,
"width": 600,
"url": "https://....."
}
What I'm trying to do is go through an API response, normalize the data to match my schema, then iterate through it and update it in the database. The code that I have works fine for inserts, but completely fails on updates:
def persist_track(track_params, sort_order) do
album_art_params = transform_json_album_art_params(track_params.album_art)
track_params =
track_params
|> Map.delete(:album_art)
|> Map.put(:sort_order, sort_order)
try do
Repo.get_by!(Track, sid: track_params.sid)
|> Track.changeset(track_params)
|> Ecto.Changeset.put_change(:album_art, album_art_params)
|> Repo.update!
rescue
Ecto.NoResultsError ->
Track.changeset(%Track{}, track_params)
|> Ecto.Changeset.put_change(:album_art, album_art_params)
|> Repo.insert!
error ->
raise error
end
end
The reason that I go through and update each one is primarily to refresh the data, but also to update the sort_order
if that's changed.
When that code runs, for updates, I get back:
** (RuntimeError) you are attempting to change relation :album_art of
CoolApp.Track, but there is missing data.
By default, if the parent model contains N children, at least the same
N children must be given on update. In other words, it is not possible
to orphan embed nor associated records, attempting to do so results
in this error message.
It is possible to change this behaviour by setting :on_replace when
defining the relation. See `Ecto.Changeset`'s section on related models
for more info.
I've read a bit about the :on_replace
setting and tried :delete
, but to no avail. I just want to clobber that column with the new settings each time. is this even possible?
EDIT: I went back in git history and tried to re-create my situation and discovered that seeing :on_replace
to :delete
actually does solve the problem. Because I didn't commit the exact situation that I was in, I'm not 100% sure if I was missing some other minor issue. I've got to make it a habit to hold onto my state better for questions on here.
来源:https://stackoverflow.com/questions/33176560/updating-records-that-use-embeds-many