Hi everyone,
I’ve been struggling with an Ecto error while trying to associate projects
with a task
in my project management app. When I attempt to save a task with associated projects, I encounter the following error:
(ArgumentError) cannot put assoc projects
, assoc projects
not found. Make sure it is spelled correctly and that the association type is not read-only
Context:
Projects:
has_many :project_tasks, TaskManager.TaskProject
has_many :tasks, through: [:project_tasks, :task]
alias TaskManager.Repo
schema "tasks" do
field :name, :string
field :description, :string
field :status, Ecto.Enum, values: [:backlog, :not_started, :in_progress, :completed], default: :backlog
field :position, :integer, default: 0
field :deadline, :date
field :duration, :integer, default: 0
field :progress, :integer, default: 0
field :priority, Ecto.Enum, values: [:low, :medium, :high, :critical], default: :medium
belongs_to :workspace, TaskManager.Workspace
belongs_to :kanban_column, TaskManager.KanbanColumn, foreign_key: :kanban_column_id, on_replace: :nilify
belongs_to :user, TaskManager.Accounts.User
belongs_to :parent, __MODULE__, foreign_key: :parent_id
has_many :children, __MODULE__, foreign_key: :parent_id
has_many :task_projects, TaskManager.TaskProject
has_many :projects, through: [:task_projects, :project]
timestamps()
end
def changeset(task, attrs) do
task
|> cast(attrs, [
:name,
:description,
:status,
:kanban_column_id,
:position,
:user_id,
:deadline,
:duration,
:progress,
:priority,
:parent_id,
:workspace_id
])
|> validate_required([:name, :user_id, :workspace_id])
|> validate_inclusion(:progress, 0..100)
|> validate_change(:deadline, fn _, deadline ->
if deadline && Date.compare(deadline, Date.utc_today()) == :lt do
[deadline: "cannot be in the past"]
else
[]
end
end)
|> validate_circular_dependency()
|> assoc_constraint(:kanban_column)
|> assoc_constraint(:user)
|> assoc_constraint(:parent)
|> put_assoc(:projects, Map.get(attrs, "projects", []))
end
In live view:
def handle_event("save_task_projects", %{"task_id" => task_id, "project_ids" => project_ids}, socket) do
task_id = String.to_integer(task_id)
project_ids = Enum.map(project_ids, &String.to_integer/1)
case Tasks.assign_projects_to_task(task_id, project_ids) do
{:ok, updated_task} ->
updated_task = Repo.preload(updated_task, :projects)
send(self(), {:task_updated, updated_task})
{:noreply, stream_insert(socket, :tests, updated_task)}
{:error, reason} ->
IO.inspect(reason, label: "Assign Projects Error")
{:noreply, put_flash(socket, :error, "Failed to assign projects to task.")}
end
end
Table:
defmodule TaskManager.TaskProject do
use Ecto.Schema
import Ecto.Changeset
@primary_key false
schema "projects_tasks" do
belongs_to :project, TaskManager.Projects.Project, primary_key: true
belongs_to :task, TaskManager.Tasks.Task, primary_key: true
timestamps()
end
def changeset(task_project, attrs) do
task_project
|> cast(attrs, [:project_id, :task_id])
|> validate_required([:project_id, :task_id])
|> assoc_constraint(:project)
|> assoc_constraint(:task)
end
end
Migration:
defmodule TaskManager.Repo.Migrations.CreateProjectsTasksJoinTable do
use Ecto.Migration
def change do
create table(:projects_tasks, primary_key: false) do
add :task_id, references(:tasks, on_delete: :delete_all), primary_key: true
add :project_id, references(:projects, on_delete: :delete_all), primary_key: true
timestamps()
end
end
end
The error occurs when I try to save a task with projects in my LiveView. For example:
- I select a task and attempt to assign it to one or more projects using a dropdown form.
- When I submit the form, the error is thrown.
Thank you very much in advance!