I am trying to update a Course
's CourseTierGroups
, which is a join table with an extra property, order
.
This is the structure of the update:
%{
"course_tier_groups" => [
%{
"id" => 1,
"order" => 1,
"tier_group" => %{
"description" => "Fuga...",
"id" => 1,
"tag" => "tgatque_et",
"tiers" => [],
"title" => "TG-Hic aliquid quas cupiditate accusantium nam et quos minus tempora."
}
},
%{
"id" => 2,
"order" => 0,
"tier_group" => %{
"description" => "Nihil...",
"id" => 2,
"tag" => "tgharum_maxime_vero_animi_ad",
"tiers" => [],
"title" => "TG-Et sunt perferendis consequatur."
}
}
]
}
This is the CourseTierGroup
schema:
schema "course_tier_group" do
belongs_to(:course, Course)
belongs_to(:tier_group, TierGroup)
field(:order, :integer)
timestamps()
end
And changeset:
def changeset(%CourseTierGroup{} = struct, params \\ %{}) do
struct
|> Repo.preload([:tier_group])
|> cast(params, [:order])
|> validate_required([:order])
|> cast_assoc(:tier_group, required: true, with: &TierGroup.changeset/2)
|> unique_constraint([:course_id, :tier_group_id])
|> unique_constraint([:course_id, :order])
end
This is the relationship in the Course
schema:
schema "course" do
...
has_many(:course_tier_groups, CourseTierGroup, on_replace: :delete)
...
end
And this is the Course
changeset:
def changeset(%Course{} = struct, params \\ %{}) do
struct
|> Repo.preload([:scopes, :type, :course_tier_groups, :topic_pills])
|> cast(params, [:title, :description, :tag, :type_metadata])
|> put_scopes
|> put_type
|> put_topic_pills
|> cast_assoc(:course_tier_groups, with: &CourseTierGroup.changeset/2)
|> validate_required([:title, :description, :tag])
|> unique_constraint([:tag])
end
In the original data, the order is switched around (id = 1 has order 0 and id = 2 has order 1).
The tier group itself is not changed, which is picked up correctly by Ecto. It also detects that I am trying to update the order. However, from what I’ve read, I was expecting the course’s ID in course_tier_groups to be filled in by Ecto, but it doesn’t do it.
It gives me the following error due to the fact that course_id is null and there already exists a record with course_id = nil and order = 1 (The one before the update):
#Ecto.Changeset<
action: :update,
changes: %{
course_tier_groups: [
#Ecto.Changeset<
action: :update,
changes: %{course_id: nil, order: 1},
errors: [
course_id: {"has already been taken",
[
constraint: :unique,
constraint_name: "course_tier_group_course_id_order_index"
]}
],
data: #LLServer.Schema.CourseTierGroup<>,
valid?: false
>,
#Ecto.Changeset<
action: :update,
changes: %{order: 0},
errors: [],
data: #LLServer.Schema.CourseTierGroup<>,
valid?: true
>
]
},
errors: [],
data: #LLServer.Schema.Course<>,
valid?: false
>
I am expecting course_id
to be filled in with the ID of the course that is being updated.
How should I deal with this? Should I also pass course_id
in my update map?