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

问题描述

我正在学习 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,data: #Tailwind.Amenities.Amenity<>,valid?: true>
    ],parkings: [
      #Ecto.Changeset<action: :update,valid?: true>
    ]
  },errors: [
    address: {"can't be blank",[validation: :required]},price: {"can't be blank",number_of_rooms: {"can't be blank",lat: {"can't be blank",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 函数只是从 db 中获取具有该 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,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) 会返回一个无效的变更集,该变更集不会被任何后续调用进一步修改(因为它已经无效。)

它被直接传递给 Repo.insert/2 返回 {: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

  ...

您看到的错误可能是由堆栈向下引起的,但原因与上述相同。