在Phoenix / Ecto中更改关联而不转换关联

时间:2018-06-12 19:29:36

标签: elixir phoenix-framework ecto

我正在尝试向Phoenix应用添加duplicate函数,该应用会获取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

我不想操纵用户关联中的字段。我只是想改变哪个用户关联。我相信这应该是可能的,而不会抛出关联(有一个blog post关于做到这一点)但我无法弄清楚如何让这个错误消失。我错过了什么?

代码的其他相关部分(简化):

变更集功能:

  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 :(得分:0)

关联宏给出options for how to handle replacing associations。默认情况下是引发错误,但如果您希望将其更改为正常的外键定义,则可以将:on_replace设置为:update。所以它看起来像:

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

我有一个跟进问题:错误还意味着Page结构已经是来自DB的加载记录。我不认为您想尝试重新插入相同的记录,而是通过params地图创建一个新的页面,从原始页面结构中复制数据。

即使它不是现有记录,而是某种中介%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