Phoenix-Framework : 在创建 child 模型时转换、关联和 nil 检查 Ecto parent 模型
Phoenix-Framework : transform, associate and nil check Ecto parent model when creating child model
我从 Rails 来到 Phoenix 框架。到目前为止,这是一个相当容易的过渡。不过,Phoenix 较新,我很难找到一些具体信息:
我正在使用我的 Phoenix 应用程序作为 API 服务层。
我希望我的 UI 表单(和传入的 curl 请求)使用 virtual field
来查找关联的 parent 模型,并使用 child 模型的 changeset
填充适当的属性。到目前为止,还不错:
在我的 child 模型中:
schema "child" do
field :parent_name, :string, virtual: true
belongs_to :parent, MyApp.Parent
end
...
before_insert :find_and_fill_parent
def find_and_fill_parent(changeset) do
parent = MyApp.Repo.get_by(
MyApp.Parent,
parent_name: changeset.changes.parent_name
)
changeset
|> Ecto.Changeset.put_change(:parent_id, parent.id)
end
这就是我卡住的地方。我不想在没有 parent 的情况下创建 child。我如何以及在哪里 nil
检查 parent 模型?尽管有条件语句,我尝试过的所有内容都已无条件地阻止或允许创建。
看来我需要在检查之前预加载 parent 模型,因为 Phoenix 旨在防止像我这样的人滥用延迟加载。不幸的是,我不知道一般正确的加载模式是什么,所以我不确定如何在这里应用它。 (我正在使用 MySQL,如果相关的话)
感谢有关查看位置和查看内容的提示和提示,以帮助我解决这个问题!谢谢!
---编辑---
根据@Gazler 的建议,我已确保我的 child 模型迁移具有参考:
create table(:child) do
add :parent_id, references(:parent)
end
我还是有点迷茫 -- 我想通过 parent 字段 parent_name
("Jane Doe"
) 找到 parent
,确保 parent 模型存在,并使用 parent_id
(1
) 关联 child。我不确定如何使用虚拟字段触发这些操作。
因此,我不确定如何构建查找 parent、建立关联和检查外键验证的结构,因为原始参数中永远不会存在外键。想法?
非常感谢。
---已解决---
使用@Gazler 的更新答案,我可以在没有虚拟属性或 before_insert.
的 child 控制器中成功检查我的 parent 模型
def create(conn, %{"post" => post_params}) do
user = Repo.get_by(User, %{name: post_params["name"]})
if is_nil(user) do
changeset = Post.changeset(%Post{}, post_params)
else
changeset = build(user, :posts) |> Post.changeset(post_params)
end
etc
end
这完全按照我的需要验证了传入的参数!谢谢@Gazler!
before_insert
可能不是执行此操作的正确位置。
您正在使用 belongs_to
关联,但是如果您在另一侧包含 has_many
关联,那么您可以使用 build/3 填写父 ID:
build(user, :posts)
这只是一个函数,用于填充结构的 post_id
字段 - 它不会验证 user
是否实际存在。
如果您想在创建 post 之前确保用户存在,那么您可以在您的变更集上使用 foreign_key_constraint/3:
cast(comment, params, ~w(user_id), ~w())
|> foreign_key_constraint(:user_id)
这将在数据库级别确保父项的记录存在。如果您的迁移看起来像这样,您需要在数据库中创建索引:
create table(:posts) do
add :user_id, references(:user)
end
编辑
不需要虚拟属性。您的控制器操作应类似于:
def create(conn, %{"name" => name, "post" => post_params}) do
user = Repo.get_by(User, %{name: name})
changeset = build(user, :posts) |> Post.changeset(post_params)
case Repo.insert(changeset) do
{:ok, post} -> ...
{:error, changeset} -> #if constraint is violated then the error will be in the changeset
end
end
我从 Rails 来到 Phoenix 框架。到目前为止,这是一个相当容易的过渡。不过,Phoenix 较新,我很难找到一些具体信息:
我正在使用我的 Phoenix 应用程序作为 API 服务层。
我希望我的 UI 表单(和传入的 curl 请求)使用 virtual field
来查找关联的 parent 模型,并使用 child 模型的 changeset
填充适当的属性。到目前为止,还不错:
在我的 child 模型中:
schema "child" do
field :parent_name, :string, virtual: true
belongs_to :parent, MyApp.Parent
end
...
before_insert :find_and_fill_parent
def find_and_fill_parent(changeset) do
parent = MyApp.Repo.get_by(
MyApp.Parent,
parent_name: changeset.changes.parent_name
)
changeset
|> Ecto.Changeset.put_change(:parent_id, parent.id)
end
这就是我卡住的地方。我不想在没有 parent 的情况下创建 child。我如何以及在哪里 nil
检查 parent 模型?尽管有条件语句,我尝试过的所有内容都已无条件地阻止或允许创建。
看来我需要在检查之前预加载 parent 模型,因为 Phoenix 旨在防止像我这样的人滥用延迟加载。不幸的是,我不知道一般正确的加载模式是什么,所以我不确定如何在这里应用它。 (我正在使用 MySQL,如果相关的话)
感谢有关查看位置和查看内容的提示和提示,以帮助我解决这个问题!谢谢!
---编辑---
根据@Gazler 的建议,我已确保我的 child 模型迁移具有参考:
create table(:child) do
add :parent_id, references(:parent)
end
我还是有点迷茫 -- 我想通过 parent 字段 parent_name
("Jane Doe"
) 找到 parent
,确保 parent 模型存在,并使用 parent_id
(1
) 关联 child。我不确定如何使用虚拟字段触发这些操作。
因此,我不确定如何构建查找 parent、建立关联和检查外键验证的结构,因为原始参数中永远不会存在外键。想法?
非常感谢。
---已解决---
使用@Gazler 的更新答案,我可以在没有虚拟属性或 before_insert.
def create(conn, %{"post" => post_params}) do
user = Repo.get_by(User, %{name: post_params["name"]})
if is_nil(user) do
changeset = Post.changeset(%Post{}, post_params)
else
changeset = build(user, :posts) |> Post.changeset(post_params)
end
etc
end
这完全按照我的需要验证了传入的参数!谢谢@Gazler!
before_insert
可能不是执行此操作的正确位置。
您正在使用 belongs_to
关联,但是如果您在另一侧包含 has_many
关联,那么您可以使用 build/3 填写父 ID:
build(user, :posts)
这只是一个函数,用于填充结构的 post_id
字段 - 它不会验证 user
是否实际存在。
如果您想在创建 post 之前确保用户存在,那么您可以在您的变更集上使用 foreign_key_constraint/3:
cast(comment, params, ~w(user_id), ~w())
|> foreign_key_constraint(:user_id)
这将在数据库级别确保父项的记录存在。如果您的迁移看起来像这样,您需要在数据库中创建索引:
create table(:posts) do
add :user_id, references(:user)
end
编辑
不需要虚拟属性。您的控制器操作应类似于:
def create(conn, %{"name" => name, "post" => post_params}) do
user = Repo.get_by(User, %{name: name})
changeset = build(user, :posts) |> Post.changeset(post_params)
case Repo.insert(changeset) do
{:ok, post} -> ...
{:error, changeset} -> #if constraint is violated then the error will be in the changeset
end
end