A problem with form submission of a many_to_many association

Hello,

Recently wanted to build a multiple checkbox input form, making it possible to reflect on a many to many association on the modeling.

I am having an error that’s mentioned on the forum previously, and mentioned solution did not work, maybe because, as is, I am not using a put_assoc but cast_assoc instead.

  def create(conn, %{"non_gov_org" => non_gov_org_params}) do
    non_gov_org_params |> IO.inspect()

    case Profiles.create_non_gov_org(non_gov_org_params) do
      {:ok, non_gov_org} ->
        conn
        |> put_flash(:info, "Non gov org created successfully.")
        |> redirect(to: Routes.non_gov_org_path(conn, :show, non_gov_org))

      {:error, %Ecto.Changeset{} = changeset} ->
        changeset |> IO.inspect()
        data = Profiles.load_causes(changeset.data)
        all_causes = Meta.list_causes()

        conn
        |> put_flash(:error, "Error!")
        |> render("new.html", changeset: %{changeset | data: data}, causes: all_causes)
    end
  end

Code is entering the :error case below, and IO.inspect is giving the detail for the state of the changeset at that execution.

Here is the inspection output:

#Ecto.Changeset<
  action: :insert,
  changes: %{
    causes: [
      #Ecto.Changeset<action: :update, changes: %{}, errors: [],
       data: #FutureMadeConcerts.Meta.Cause<>, valid?: true>
    ],
    description: "xsdf",
    headquarters: "sdfsdf",
    name: "xxxx"
  },
  errors: [causes: {"is invalid", [validation: :assoc, type: {:array, :map}]}],
  data: #FutureMadeConcerts.Profiles.NonGovOrg<>,
  valid?: false
>

Here, causes can have multiple non-governmental organisations and vice versa through a third table/schema.

note: They are as follows (cause is generated with schema, NonGovOrg is via html generators.)

#Cause

defmodule FutureMadeConcerts.Meta.Cause do
  use Ecto.Schema
  import Ecto.Changeset

  alias FutureMadeConcerts.Profiles.NonGovOrg

  schema "causes" do
    field :description, :string
    field :image_url, :string
    field :info_url, :string
    field :name, :string

    many_to_many :non_gov_orgs, NonGovOrg,
      join_through: "causes_ngos"

    timestamps()
  end

  @doc false
  def changeset(cause, attrs) do
    cause
    |> cast(attrs, [:name, :description, :image_url, :info_url])
    |> cast_assoc(:non_gov_orgs, required: false)
    |> validate_required([:name, :description, :image_url, :info_url])
  end
end

Cause:

defmodule FutureMadeConcerts.Profiles.NonGovOrg do
  use Ecto.Schema
  import Ecto.Changeset

  alias FutureMadeConcerts.Meta.Cause

  schema "non_gov_orgs" do
    field :description, :string
    field :name, :string
    field :headquarters, :string
    field :image_url, :string
    field :website_url, :string
    field :number_of_followers, :integer, default: 0
    field :number_of_open_projects, :integer, default: 0

    many_to_many :causes, Cause,
        join_through: "causes_ngos"
    timestamps()
  end

  @doc false
  def changeset(non_gov_org, attrs) do
    non_gov_org
    |> cast(attrs, [:name, :description, :headquarters, :image_url, :website_url])
    |> cast_assoc(:causes, required: false)
    |> validate_required([:name, :description, :headquarters])
  end
end

You can find the code at this repo. It’s a re-write of https://collabomo.de, an app to start and experiment a demand-aware framework.

Bests,
@