Я использую Эликсир и Феникс. У меня есть схемы Venues.Team
и Accounts.Job
с отношением many-to-many
между ними.
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
Я хочу иметь возможность включить аргумент jobs
в параметр attrs
при обновлении Team
. Это должно вставить ассоциацию в поле :employees
.
Я могу сделать это в интерактивной оболочке эликсира (из документации 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()
Какой самый лучший / самый чистый / самый обычный способ сделать это?