Updating records that use embeds_many

喜欢而已 提交于 2019-12-24 03:52:13

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!