gpt4 book ai didi

json - 为自引用 Ecto 模型构建 JSON 映射

转载 作者:行者123 更新时间:2023-12-03 22:34:28 24 4
gpt4 key购买 nike

我有一个这样的 Ecto 模型:

defmodule Project.Category do
use Project.Web, :model

schema "categories" do
field :name, :string
field :list_order, :integer
field :parent_id, :integer
belongs_to :menu, Project.Menu
has_many :subcategories, Project.Category, foreign_key: :parent_id
timestamps
end

@required_fields ~w(name list_order)
@optional_fields ~w(menu_id parent_id)

def changeset(model, params \\ :empty) do
model
|> cast(params, @required_fields, @optional_fields)
end
end

如您所见,类别模型可以通过子类别原子引用自身。

这是与此模型关联的 View :
defmodule Project.CategoryView do
use Project.Web, :view

def render("show.json", %{category: category}) do
json = %{
id: category.id,
name: category.name,
list_order: category.list_order
parent_id: category.parent_id
}
if is_list(category.subcategories) do
children = render_many(category.subcategories, Project.CategoryView, "show.json")
Map.put(json, :subcategories, children)
else
json
end
end
end

我对子类别有一个 if 条件,这样当它们没有被预加载时我可以很好地使用 Poison。

最后,这是我调用此 View 的 2 个 Controller 函数:
defmodule Project.CategoryController do
use Project.Web, :controller

alias Project.Category

def show(conn, %{"id" => id}) do
category = Repo.get!(Category, id)
render conn, "show.json", category: category
end

def showWithChildren(conn, %{"id" => id}) do
category = Repo.get!(Category, id)
|> Repo.preload [:subcategories, subcategories: :subcategories]
render conn, "show.json", category: category
end
end
show功能工作正常:
{
"parent_id": null,
"name": "a",
"list_order": 4,
"id": 7
}

然而,我的 showWithChildren由于我使用预加载的方式,函数仅限于 2 级嵌套:
{
"subcategories": [
{
"subcategories": [
{
"parent_id": 10,
"name": "d",
"list_order": 4,
"id": 11
}
],
"parent_id": 7,
"name": "c",
"list_order": 4,
"id": 10
},
{
"subcategories": [],
"parent_id": 7,
"name": "b",
"list_order": 9,
"id": 13
}
],
"parent_id": null,
"name": "a",
"list_order": 4,
"id": 7
}

例如,上面的类别项 11 也有子类别,但我无法访问它们。这些子类别本身也可以有子类别,因此层次结构的潜在深度为 n。

我知道我需要一些递归魔法,但由于我是函数式编程和 Elixir 的新手,我无法理解它。任何帮助是极大的赞赏。

最佳答案

您可以考虑在 View 中进行预加载,因此它以递归方式工作:

def render("show.json", %{category: category}) do
%{id: category.id,
name: category.name,
list_order: category.list_order
parent_id: category.parent_id}
|> add_subcategories(category)
end

defp add_subcategories(json, %{subcategories: subcategories}) when is_list(subcategories) do
children =
subcategories
|> Repo.preload(:subcategories)
|> render_many(Project.CategoryView, "show.json")
Map.put(json, :subcategories, children)
end

defp add_subcategories(json, _category) do
json
end

请记住,这并不理想,原因有两个:
  • 理想情况下,您不想在 View 中进行查询(但这是递归的,因此在 View 渲染中更容易搭载)
  • 您将针对第二级子类别
  • 发出多个查询

    有一本书叫 SQL Antipatterns而且,如果我没记错的话,它涵盖了如何编写树结构。您的示例在其中一个免费章节中作为反模式公开。这是一本优秀的书,他们探索了所有反模式的解决方案。

    PS:你要 show_with_children而不是 showWithChildren .

    关于json - 为自引用 Ecto 模型构建 JSON 映射,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32380314/

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