我正在尝试在Ecto 2中创建一个自引用many_to_many
关系。我关注了this blogpost并让它工作到目前为止。但是,尝试更新与Ecto.Changeset.put_assoc
的关联总是会导致错误。我不明白为什么。
这是设置:
首先是创建用户的迁移和联系人的关联表(每个用户都可以拥有多个也是用户的联系人):
# priv/repo/migrations/create_users_table.ex
defmodule MyApp.Repo.Migrations.CreateUsersTable do
use Ecto.Migration
def change do
create table(:users) do
add :username, :string
end
create unique_index(:users, [:username])
end
end
# priv/repo/migrations/create_contacts_table.ex
defmodule MyApp.Repo.Migrations.CreateContactsTable do
use Ecto.Migration
def change do
create table(:contacts) do
add :user_id, references(:users, on_delete: :nothing), primary_key: true
add :contact_id, references(:users, on_delete: :nothing), primary_key: true
timestamps()
end
end
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
这现在有效:
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
但是我可以预加载关联,我也可以手动创建关联。所以我可以循环遍历contact_ids并执行此操作:
result = user
|> Ecto.Changeset.change
|> Ecto.Changeset.put_assoc(:contacts, [Contact.changeset(%Contact{}, %{user_id: user_id, contact_id: contact_id})])
|> Repo.insert
我在这里做错了什么?
答案 0 :(得分:2)
我无法通过自己的协会重现问题。我觉得您可能在某个时候使用%MyApp.Contact{}
代替%MyApp.User{}
?你能检查一下并报告回来吗?
我注意到了一些事情(但不会产生此错误):您正试图将MyApp.User
个变更集置于:contacts
关联中,这需要MyApp.Contact
个变更集。
您可以尝试使用many_to_many
。您可以确定使用它返回MyApp.User
,因此这样的边缘情况会更少。无论如何,它专门为这些类型的协会而制作。
MyApp.User架构:
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_keys
选项,因为我认为没有它,Ecto可能会在这种情况下感到困惑。我建议你尝试使用和不使用它。
使用many_to_many
,您可以将MyApp.User
更改集直接插入:contacts
关联,这似乎就是您想要做的。