gpt4 book ai didi

database - Elixir Phoenix 更新嵌套多对多关联

转载 作者:行者123 更新时间:2023-12-02 21:40:27 31 4
gpt4 key购买 nike

我正在使用 Elixir 和 Phoenix。我有架构 Venues.TeamAccounts.Job,它们之间具有多对多 关系。

defmodule Runbook.Venues.Team do
use Ecto.Schema
import Ecto.Changeset

schema "teams" do
field :name, :string
belongs_to :venue, Runbook.Venues.Venue
many_to_many :employees, Runbook.Accounts.Job, join_through: "jobs_teams"

timestamps()
end

@doc false
def changeset(team, attrs) do
team
|> cast(attrs, [:name])
|> validate_required([:name])
end
end

defmodule Runbook.Accounts.Job do
use Ecto.Schema
import Ecto.Changeset

schema "jobs" do
field :role, :string
belongs_to :user, Runbook.Accounts.User
belongs_to :venue, Runbook.Venues.Venue
many_to_many :teams, Runbook.Venues.Team, join_through: "jobs_teams"

timestamps()
end

@doc false
def changeset(job, attrs) do
job
|> cast(attrs, [:role])
|> validate_required([:role])
end
end

我有默认的 Venues.update_team/2 方法:

def update_team(%Team{} = team, attrs) do
team
|> Team.changeset(attrs)
|> Repo.update()
end

我希望在更新团队时能够在attrs参数中包含jobs参数。这应该将关联插入到 :employees 字段中。

我可以在交互式 Elixir shell 中执行此操作(来自 ElixirSchool 文档)

team = Venues.get_team!(1)
team_changeset = Ecto.Changeset.change(team)
team_add_employees_changeset = team_changeset |> put_assoc(:employees, [job])
Repo.update!(team_add_employees_changeset)

但我不确定如何将其抽象为 update_team 方法,该方法无需数据库调用即可构建变更集。

当我尝试这样做时:

%Team{}
|> Team.changeset(%{id: 1, name: "Floor"})
|> put_assoc(:employees, [job])
|> Repo.update()

我收到一条错误消息,提示 :employees 关联未加载:

** (Ecto.NoPrimaryKeyValueError) struct `%Runbook.Venues.Team{__meta__: #Ecto.Schema.Metadata<:built, "teams">, employees: #Ecto.Association.NotLoaded<association :employees is not 
loaded>, id: nil, inserted_at: nil, name: nil, updated_at: nil, venue: #Ecto.Association.NotLoaded<association :venue is not loaded>, venue_id: nil}` is missing primary key value
(ecto) lib/ecto/repo/schema.ex:898: anonymous fn/3 in Ecto.Repo.Schema.add_pk_filter!/2
(elixir) lib/enum.ex:1948: Enum."-reduce/3-lists^foldl/2-0-"/3
(ecto) lib/ecto/repo/schema.ex:312: Ecto.Repo.Schema.do_update/4

我可以这样做:

changeset = Team.changeset(%Team{}, %{id: 1, name: "Floor"})
team = Venues.get_team!(1) |> Repo.preload(:employees)
preloaded_changeset = %Ecto.Changeset{changeset | data: team}
preloaded_changeset |> put_assoc(:employees, [job]) |> Repo.update()

(未经测试,以下的更清晰版本)

 %Ecto.Changeset{tc | data: Venues.get_team!(1) |> Repo.preload(:employees)} |> put_assoc(:employees, [job]) |> Repo.update()

最好/最干净/最传统的方法是什么?

最佳答案

您可以使用 Ecto.Changeset.cast_assoc/3 进行更新

def update_team(%Team{} = team, attrs) do
team
|> Repo.preload(:employees) # has to be preloaded to perform update
|> Team.changeset(attrs)
|> Ecto.Changeset.cast_assoc(:employees, with: &Job.changeset/2)
|> Repo.update
end

更新表单

<%= form_for @changeset, @action, fn f -> %>
<%= if @changeset.action do %>
<div class="alert alert-danger">
<p>Oops, something went wrong! Please check the errors below.</p>
</div>
<% end %>

<%= label f, :name %>
<%= text_input f, :name %>
<%= error_tag f, :name %>

<div class="form-group">
<%= inputs_for f, :job, fn cf -> %>
<%= label cf, :job_param_1 %>
<%= text_input cf, :job_param_1 %>
<%= error_tag cf, :job_param_1 %>
<% end %>
</div>

<div class="form-group">
<%= inputs_for f, :job, fn cf -> %>
<%= label cf, :job_param_2 %>
<%= text_input cf, :job_param_2 %>
<%= error_tag cf, :job_param_2 %>
<% end %>
</div>

<div>
<%= submit "Save" %>
</div>
<% end %>

关于database - Elixir Phoenix 更新嵌套多对多关联,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58726460/

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