phoenix-framework 与复选框的多对多关系在验证失败时引发 argumentError

phoenix-framework many-to-many relationship with checkbox raises argumentError when validation fails

我正在学习 elixir 和 phoenix 框架以及一个具有添加房间形式的应用程序。房间可能与停车位和便利设施有 many-to-many 关系。停车场和便利设施就像通过复选框选中的标签,除了房间有价格、地址、纬度、经度和其他字段。当我填写所有正确的值时,一切都运作良好,并创造了空间。但是抛出 当我检查停车或便利设施字段并将其他字段留空并提交时出现 ArgumentError。

Request: POST /rooms
** (exit) an exception was raised:
    ** (ArgumentError) lists in Phoenix.HTML and templates may only contain integers representing bytes, binaries or other lists, got invalid entry: #Ecto.Changeset<action: :update, changes: %{}, errors: [], data: #Tailwind.Parkings.Parking<>, valid?: true>

这是验证失败时抛出的变更集。

Ecto.Changeset<
  action: nil,
  changes: %{
    amenities: [
      #Ecto.Changeset<action: :update, changes: %{}, errors: [],
       data: #Tailwind.Amenities.Amenity<>, valid?: true>
    ],
    parkings: [
      #Ecto.Changeset<action: :update, changes: %{}, errors: [],
       data: #Tailwind.Parkings.Parking<>, valid?: true>
    ]
  },
  errors: [
    address: {"can't be blank", [validation: :required]},
    price: {"can't be blank", [validation: :required]},
    number_of_rooms: {"can't be blank", [validation: :required]},
    lat: {"can't be blank", [validation: :required]},
    long: {"can't be blank", [validation: :required]}
  ],
  data: #Tailwind.Rooms.Room<>,
  valid?: false
>

我最好的猜测是变化中的便利设施和停车位值是问题的原因。便利设施和停车场领域有一个变更集,而不是简单的列表。尽管我的猜测是正确的,但我对它为什么会这样以及它应该如何表现没有足够的理解。

我正在我的上下文中添加与 put_assoc 函数的关联。

def create_room(attrs \ %{}) do
    %Room{}
    |> Room.changeset(attrs)
    |> put_parking_association(attrs["parkings"])
    |> put_amenity_association(attrs["amenities"])
    |> Repo.insert()
  end

和 put_amenity_association 函数只是从数据库中获取具有该 ID 的所有值并添加与它们的关联。

 defp put_amenity_association(changeset, attrs) do
    amenities = Tailwind.Amenities.get_amenities(attrs)
    Ecto.Changeset.put_assoc(changeset, :amenities, amenities)
  end


 def get_amenities(ids) do
    Repo.all(from a in Tailwind.Amenities.Amenity, where: a.id in ^ids)
  end

我正在模板中渲染这样的表单。

      <%= for amenity <- @amenities do %>
      <div class="flex items-center py-2 sm:w-1/4">
        <%= checkbox f, :amenities,
         checked_value: amenity.id,
         hidden_input: false,
         name: "room[amenities][]",

         class: "h-5 w-5 focus:ring-gray-900 focus:ring-1 bg-gray-900 text-gray-600" %>
        <span class="ml-3 text-sm"><%= amenity.name %></span>

      <% end %>
    <span class="text-red-600 px-2">
      <%= error_tag f, :amenities  %>
    </span>

我纠结了几天,没有任何结果,感到迷茫。对这件事的一点点阳光对前进会有很大的帮助。

更新:

使用可接受的解决方案,我将 create_room 函数更改如下:

def create_room(attrs \ %{}) do
    case Room.changeset(%Room{}, attrs) do
      %Ecto.Changeset{valid?: false} = changes ->
        changes
        |> apply_action(:insert)

      # |> Repo.insert()

      changeset ->
        changeset
        |> put_parking_association(attrs["parkings"])
        |> put_amenity_association(attrs["amenities"])
        |> IO.inspect()
        |> Repo.insert()
    end
  end

当缺少必填字段时,Room.changeset(attrs) returns 一个无效的变更集,不会被任何后续调用进一步修改(因为它已经无效。)

它直接传递给 Repo.insert/2 returns {:error, changeset} 返回。

所以你需要的可能是不仅要处理快乐的路径,还要处理错误。

def create_room(attrs \ %{}) do
  case Room.changeset(%Room{}, attrs) do
    %Ecto.Changeset{valid?: false} -> handle_error(...)

    changeset ->
      changeset
      |> put_parking_association(attrs["parking"])
      |> put_amenity_association(attrs["amenities"])
      |> Repo.insert()
  end

  ...

您看到的错误可能是在堆栈的下方引起的,但原因与上述错误并列。