Изменение ассоциации без кастинга в Фениксе / Экто - PullRequest
0 голосов
/ 12 июня 2018

Я пытаюсь добавить функцию duplicate в приложение Phoenix, которая принимает объект Page и owner_id и создает новый Page с идентичными полями, но принадлежит кому-то другому.Прямо сейчас это выглядит так:

  def duplicate(%Page{} = page, new_owner_id) when is_integer(new_owner_id) do
    page
    |> Page.changeset(%{owner_id: new_owner_id})
    |> Repo.insert()
  end

Тестирование вызывает эту ошибку:

(RuntimeError) attempting to cast or change association `owner` from `Page` that was not loaded. Please preload your associations before manipulating them through changesets

Я не хочу манипулировать полями внутри пользователяассоциация.Я просто хочу изменить, какой пользователь связан.Я полагаю, что это возможно без использования ассоциации (в блоге 1014 * о том, как делать именно это), но я не могу понять, как устранить эту ошибку.Чего мне не хватает?

Другие соответствующие части кода (упрощенно):

Функция изменения:

  def changeset(%Page{} = page, attrs) do
    page
    |> cast(attrs, [:owner_id])
    |> foreign_key_constraint(:owner_id)
  end

Схема:

  schema "pages" do
    belongs_to :owner, User

    timestamps()
  end

Миграция:

  def change do
    create table(:pages) do
      add :owner_id, references(:users, on_delete: :nothing)

      timestamps()
    end

    create index(:pages, [:owner_id])
  end

1 Ответ

0 голосов
/ 13 июня 2018

Макросы ассоциации дают опции для обработки заменяющих ассоциаций .По умолчанию выдается ошибка, но вы можете установить :on_replace на :update, если хотите, чтобы она изменилась, как обычное определение внешнего ключа.Так это будет выглядеть так:

belongs_to :owner, User, [on_replace: :update]

У меня есть дополнительный вопрос: ошибка также подразумевает, что структура Page уже загружена из БД.Я не думаю, что вы захотите попытаться повторно вставить ту же запись, а создать новую страницу с помощью карты параметров, копируя данные из исходной структуры страницы.

Даже если это не существующая запись, но вместо какого-то посредника %Page{} (без первичного ключа, например) вы можете подумать о том, чтобы поместить копирующий логин в саму функцию дублирования:

 def duplicate(%Page{example_data: example_data} = original_page, new_owner_id) when is_integer(new_owner_id) do
    %Page{}
    |> Page.changeset(%{example_data: example_data, owner_id: new_owner_id})
    |> Repo.insert()
  end
...