put_assoc жалуется на неправильный тип - PullRequest
0 голосов
/ 26 июня 2019

У меня есть схемы Ecto, представляющие ориентированный граф - есть схема Node с самообращающейся ссылкой «многие ко многим» через промежуточную схему NodeEdge. т.е.

  schema "nodes" do
    field :name, :string

    many_to_many :edges, MyApp.Node,
      join_through: MyApp.NodeEdge,
      join_keys: [source: :id, target: :id]

    timestamps()
  end

  schema "node_edges" do
    belongs_to :source, MyApp.Node
    belongs_to :target, MyApp.Node
    timestamps()
  end

со следующей начальной миграцией:

create table(:nodes) do
  add :name, :string, null: false
  timestamps()
end

create table(:node_edges) do
  add :source, references(:nodes)
  add :target, references(:nodes)
  timestamps()
end

Я пытаюсь выполнить массовую вставку кромок следующим образом:

now = NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second)

new_edges =
  Enum.map(
    result.edges,
    &%NodeEdge{
      source: curr_name,
      target: &1,
      inserted_at: now,
      updated_at: now
    }
  )

node
|> Repo.preload(:edges)
|> Ecto.Changeset.change()
|> Ecto.Changeset.put_assoc(:edges, my_edges)
|> Repo.update!()

Насколько я могу судить, это довольно близко к документации для put_assoc/4. Однако я получаю сообщение об ошибке:

** (ArgumentError) expected changeset data to be a Elixir.MyApp.Node struct, got: %MyApp.NodeEdge{__meta__: #Ecto.Schema.Metadata<:built, "node_edges">, id: nil, inserted_at: ~N[2019-06-25 23:18:35], source: "nodeA", source_id: nil, target: "nodeB", target_id: nil, updated_at: ~N[2019-06-25 23:18:35]}

Возможно, ошибка в том, что я заранее не знаю идентификаторы узлов? Или кто-нибудь может увидеть другую проблему?

1 Ответ

0 голосов
/ 26 июня 2019

Ab ошибка довольно понятна: вы создаете список NodeEdge, а затем пытаетесь применить этот список к ассоциации edges, тип Node.

Куча решений:

  • Построить список Node вместо списка NodeEdge. Поместите этот список с put_assoc

  • Добавить отношение к ребрам узла

    schema "nodes" do
    field :name, :string
    
    many_to_many :edges, MyApp.Node,
      join_through: MyApp.NodeEdge,
      join_keys: [source: :id, target: :id]
    
    has_many :node_edges, MyApp.NodeEdge, foreign_key: source
    
    timestamps()
    end
    

    Теперь поместите здесь список NodeEdge.

  • Просто создайте этот список - он абсолютно самоочевиден. Поскольку NodeEdge имеет отношения - используйте их для создания ассоциаций:

    %NodeEdge{
      source: put_assoc(:source, curr_name),
      target: put_assoc(:target, &1),
      inserted_at: now,
      updated_at: now
    }
    
    

    Создайте этот список и сохраните его.

...