Updating multiple models in a single liveview form

I have two models called “inspection” and “equipment”.
Their schemas look like this:

  schema "equipments" do
    field :name, :string
    has_many :inspections, MyApp.Inspection
    embeds_many :metrics, MyApp.Metric,
      on_replace: :delete

    timestamps(type: :utc_datetime)
  end

  schema "inspections" do
    has_many :checks, MyApp.Check
    belongs_to :equipment, MyApp.Equipment

    timestamps(type: :utc_datetime)
  end

I’m working on creating a form that would allow users to submit inspections for an equipment.

I’ve already handled the part where the inspection’s checks are rendered, validated, and submitted.

I’m now working on adding another section to the same form which would allow the user to input values for the metrics that belong to the equipment being inspected. I’m struggling with this piece at the moment. The input fields render fine, and the validations run i expect them to, but when the validation on a “metric” fails, Phoenix raises the following error message:

** (ArgumentError) could not generate inputs for :checks from MyApp.Equipment. 
Check the field exists and it is one of embeds_one, embeds_many, has_one, has_many, belongs_to or many_to_many

I’ll paste some code in the thread to show what my current approach looks like.

I’d really appreciate your thoughts if you’ve come across this before, or if you think i’m doing something in a weird way <3

defmodule MyApp.InspectionLive.FormComponent do
  use MyAppWeb, :live_component

  @impl true
  def render(assigns) do
    ~H"""
    <div>
      <.header>
        <%= "#{@title}" %>
      </.header>

      <.simple_form
        for={@form}
        id="form-submission-form"
        phx-target={@myself}
        phx-submit="save"
      >
        <input type="hidden" name="inspection[equipment_id]" value={@equipment.id} />
        <div id="checks">
          <.inputs_for :let={check} field={@form[:checks]} id={"inspection"}>
            <%= case Firedepartmentmanagement.Forms.Element.supported_type_for(check[:type].value) do %>
              <CheckComponent.render
                ...
                index={check.index}
              />
          </.inputs_for>
        </div>
        <div id="equipment-metrics">
          <h3>equipment metrics</h3>
          <input type="hidden" name="equipment[name]" value={@equipment.name} />
          <%= for {equipment_metric, index} <- Enum.with_index(@equipment.metrics) do %>
            <div class="flex space-x-1">
              <input type="hidden" name={"equipment[metrics][#{index}][id]"} value={equipment_metric.id} />
              <%= equipment_metric.name %>
              <input
                type="text"
                name={"equipment[metrics][#{index}][value]"}
                value={equipment_metric.value}
              />
            </div>
          <% end %>
        </div>
        <:actions>
          <.button phx-disable-with="Saving...">Submit inspection</.button>
        </:actions>
      </.simple_form>
    </div>
    """
  end

  @impl true
  def update(%{inspection: inspection} = assigns, socket) do
    {
      :ok,
      socket
      |> assign(assigns)
      |> assign_new(
        :form,
        fn ->
          to_form(Forms.change_inspection(inspection))
        end
      )
    }
  end

  def handle_event("save", params, socket) do
    save_inspection(socket, socket.assigns.action, params)
  end

  defp save_inspection(socket, :new, %{"inspection" => inspection_params, "equipment" => equipment_params} = params) do
    case Forms.create_inspection(params) do
      {:ok, inspection} ->
        notify_parent({:saved, inspection})

        {:noreply,
        socket
        |> put_flash(:info, "Submitted inspection successfully")
        |> push_patch(to: socket.assigns.patch)}

      {:error, %Ecto.Changeset{} = changeset} ->
        as_form = to_form(changeset)
        {:noreply, assign(socket, form: to_form(changeset))}
    end
  end

  defp notify_parent(msg), do: send(self(), {__MODULE__, msg})
end

Check out inputs_for, it is made for things like this.

I’d suggest sharing what your IO.inspect returns for socket.assigns.form and socket.assigns.inspection as well as the code for Forms.change_inspection.