gpt4 book ai didi

postgresql - 使用 Ecto 在 Phoenix 中进行自引用关联

转载 作者:行者123 更新时间:2023-11-29 13:42:54 25 4
gpt4 key购买 nike

所以我刚刚开始摆弄 Phoenix 和 Elixir。所以我已经达到了我试图让一个工作的 rest-api 端点与先决条件 JSON 一起工作的地步。

所以我有这个模块:

defmodule MyApp.Housing.Part do
use Ecto.Schema
import Ecto.Changeset

@primary_key {:id, :integer, []}
schema "parts" do
field :level, :integer
field :title, :string
belongs_to :parent, MyApp.Housing.Part
has_many :children, MyApp.Housing.Part, foreign_key: :parent_id
timestamps()
end

def changeset(part, params \\ %{}) do
part
|> cast(params, [:title, :level, :id, :parent_id])
|> put_assoc(:children, required: false)
|> put_assoc(:parent, required: false)
|> validate_required([:title, :level, :id])
end
end

以及创建表的模块

 defmodule MyApp.Repo.Migrations.CreateParts do
use Ecto.Migration

def change do
create table(:parts, primary_key: false) do
add :id, :integer, primary_key: true
add :title, :string
add :level, :integer
add :parent_id, references(:parts)
add :children, references(:parts)
timestamps()
end

create index(:parts, [:children])
create index(:parts, [:parent_id])
end
end

inteded functionality 是为了让一个部分能够有多个 child ,但只有一个 parent 。这些是在这样的 JSON 中定义的:

{"id": 10,
"title": "Matt",
"level": 0,
"children": [],
"parent_id": null}

所以我的问题如下:

  • “变更集”要求传入对象看起来像{"part":{}}否则会抛出 ActionClauseError。
  • 当按上述方式定义对象时,出现错误 children":["is invalid"]。而且我无法弄清楚如何获得有效的对象,如果可以的话我可能可以找出问题。

我在这里可能会采取错误的方法,但很乐意接受任何帮助。

最佳答案

正如@steve-pallen 所提到的,没有必要在数据库中存储对children 的任何引用。确定一个 Part 是父还是子,以及哪个 Part 是它的子或者哪个 Part 是它的父可以是完全由 parent_id 字段决定。

您在问题中描述了每个 Part“只能有一个父项,但可以有多个子项”。在您的问题中,这种关系允许多少级别并不明确:即 Part 可以既是 parent 又是 child 吗?在这种情况下,可能会有无限级的嵌套:

part1
|- part2
|- part3
|- part4

在这种情况下,part1part2 的父级,part2 本身是part3 的父级,等等。对于我的回答,我假设嵌套的数量没有限制。

在这种情况下,您的架构定义是 100% 正确的:

belongs_to :parent, MyApp.Housing.Part
has_many :children, MyApp.Housing.Part, foreign_key: :parent_id

我认为主要问题在于您的变更集功能。请记住,对于 put_assoc/3,预计 parentchildren 引用的所有模型都已存在于数据库中(请参阅 cast_assoc/3 的文档).为简单起见,我建议您不要使用 put_assoc cast_assoc 而是单独管理每个模型。如果您将变更集函数更改为此(我已删除 id 因为它不是必需的):

def changeset(part, params \\ %{}) do
part
|> cast(params, [:title, :level, :parent_id])
|> validate_required([:title, :level])
end

然后您可以通过单独执行 4 次插入来构建我上面显示的嵌套关系(更容易推理,并且可能更符合您处理来自表单或脚本的数据库更新的方式):

part1 = 
MyApp.Housing.Part.changeset(%MyApp.Housing.Part{}, %{title: "part1", level: 0, parent_id: nil})
|> Repo.insert!()

part2 =
MyApp.Housing.Part.changeset(%MyApp.Housing.Part{}, %{title: "part2", level: 0, parent_id: part1.id})
|> Repo.insert!()

part3 =
MyApp.Housing.Part.changeset(%MyApp.Housing.Part{}, %{title: "part3", level: 0, parent_id: part2.id})
|> Repo.insert!()

part4 =
MyApp.Housing.Part.changeset(%MyApp.Housing.Part{}, %{title: "part4", level: 0, parent_id: part3.id})
|> Repo.insert!()

假设我们想要获取 part2,我们可以像这样加载它及其父子:

part2 = Repo.preload(part2, [:parent, :children])
# part2.parent == %MyApp.Housing.Part{title: "part1", ...}
# part2.children == [%MyApp.Housing.Part{title: "part3", ...}]

希望这对您有所帮助!

关于postgresql - 使用 Ecto 在 Phoenix 中进行自引用关联,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52746530/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com