Elixir-Exto 2:自引用多对多和exto.Changeset.put\u assoc.如何?

Elixir-Exto 2:自引用多对多和exto.Changeset.put\u assoc.如何?,elixir,ecto,Elixir,Ecto,我正在尝试在《星外2》中创建一个自引用的many\u-to\u-many关系。我关注了blogpost,到目前为止它一直在运行。但是,尝试更新与exto.Changeset.put_assoc的关联总是会导致错误。我不明白为什么 这是设置: defmodule MyApp.User do use MyApp.Web, :model alias MyApp.Contact schema "users" do field :username, :string # A

我正在尝试在《星外2》中创建一个自引用的
many\u-to\u-many
关系。我关注了blogpost,到目前为止它一直在运行。但是,尝试更新与
exto.Changeset.put_assoc
的关联总是会导致错误。我不明白为什么

这是设置:

defmodule MyApp.User do  
  use MyApp.Web, :model
  alias MyApp.Contact

  schema "users" do
    field :username, :string
    # Add the many-to-many association
    has_many :_contacts, MyApp.Contact
    has_many :contacts, through: [:_contacts, :contact]
    timestamps
  end

  # Omitting changesets
end   

defmodule MyApp.Contact do  
  use MyApp.Web, :model

  alias MyApp.User

  schema "contacts" do
    belongs_to :user, User
    belongs_to :contact, User
  end
end 
schema "users" do
  field :username, :string

  many_to_many :contacts, MyApp.User, join_through: MyApp.Contact, join_keys: [user_id: :id, contact_id: id]
  timestamps
end
首先,迁移以创建用户和联系人的关联表(每个用户都可以有多个联系人,这些联系人也是用户):

现在型号:

defmodule MyApp.User do  
  use MyApp.Web, :model
  alias MyApp.Contact

  schema "users" do
    field :username, :string
    # Add the many-to-many association
    has_many :_contacts, MyApp.Contact
    has_many :contacts, through: [:_contacts, :contact]
    timestamps
  end

  # Omitting changesets
end   

defmodule MyApp.Contact do  
  use MyApp.Web, :model

  alias MyApp.User

  schema "contacts" do
    belongs_to :user, User
    belongs_to :contact, User
  end
end 
schema "users" do
  field :username, :string

  many_to_many :contacts, MyApp.User, join_through: MyApp.Contact, join_keys: [user_id: :id, contact_id: id]
  timestamps
end
现在可以这样做了:

user = Repo.get!(User, 1) |> Repo.preload :contacts
user.contacts  
现在,我试图解析一个逗号分隔的ID字符串,获取用户,将其转换为变更集,并将其作为联系人附加到另一个用户

# Parse string and get list of ids
contact_ids = String.split("2, 3, 4", ",") |> Enum.map(&String.trim/1)

# Get contacts
contacts = Enum.map(contact_ids, fn(id) ->
  Repo.get! User, id
end)

# Turn them into changesets
contact_changesets = Enum.map(contacts, &Ecto.Changeset.change/1)

# Update the associations
result = user |> Ecto.Changeset.change
|> Ecto.Changeset.put_assoc(:contacts, contact_changesets)
|> Repo.update
我得到的错误是

** (ArgumentError) cannot put assoc `contacts`, assoc `contacts` not found. Make sure it is spelled correctly and properly pluralized (or singularized)
    (ecto) lib/ecto/changeset.ex:568: Ecto.Changeset.relation!/4
    (ecto) lib/ecto/changeset.ex:888: Ecto.Changeset.put_relation/5
但是我可以预加载关联,也可以手动创建关联。所以我可以在联系人ID上循环并执行以下操作:

result = user 
|> Ecto.Changeset.change 
|> Ecto.Changeset.put_assoc(:contacts, [Contact.changeset(%Contact{}, %{user_id: user_id, contact_id: contact_id})])
|> Repo.insert

我做错了什么?

我无法用自己的联想重现这个问题。我有一种感觉,您可能在某个时候正在使用
%MyApp.Contact{}
而不是
%MyApp.User{}
?你能检查一下并报告吗

我注意到(但不会产生此错误):您正在尝试将
MyApp.User
更改集放入
:contacts
关联中,该关联需要
MyApp.Contact
更改集

你可以尝试使用。您可以确保使用它返回
MyApp.User
,这样就不会出现像这样的边缘情况。无论如何,它是专门为这些类型的协会而设计的

MyApp.User模式:

defmodule MyApp.User do  
  use MyApp.Web, :model
  alias MyApp.Contact

  schema "users" do
    field :username, :string
    # Add the many-to-many association
    has_many :_contacts, MyApp.Contact
    has_many :contacts, through: [:_contacts, :contact]
    timestamps
  end

  # Omitting changesets
end   

defmodule MyApp.Contact do  
  use MyApp.Web, :model

  alias MyApp.User

  schema "contacts" do
    belongs_to :user, User
    belongs_to :contact, User
  end
end 
schema "users" do
  field :username, :string

  many_to_many :contacts, MyApp.User, join_through: MyApp.Contact, join_keys: [user_id: :id, contact_id: id]
  timestamps
end
我添加了
join\u keys
选项,因为我认为没有它,在这种情况下,Ecto可能会感到困惑。我建议你试试有没有


使用
many\u to\u many
您可以将
MyApp.User
变更集直接插入
:contacts
关联中,这似乎就是你想要做的。

你有什么理由不只是使用它吗?@JustinWood我只能引用:
一个Elixir社区的成员在Slack上告诉我,这看起来更像是一对多的关系,我应该尝试在我的应用程序中这样表示。[…]在我的Phoenix应用程序的实际测试中,我注意到,使用前一个关联类型生成的查询是错误的。将其从“多”切换到“多到”后,修复了上述问题。