Я изучаю структуру эликсира и феникса с помощью приложения, которое имеет форму для добавления комнаты. номер может иметь 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 просто получает все значения с этим идентификатором из базы данных и добавляет к ним ассоциацию.
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)
возвращает недопустимый набор изменений, который больше не изменяется никакими последующими вызовами (поскольку он уже недействителен).
Он передается напрямую в 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
...
Ошибка, которую вы видите, вероятно, вызвана спуском вниз по стеку, но причина кроется в вышеизложенном.
Я понятия не имею, откуда берется apply_action/2
, но обычно для сохранения входных данных changeset: changeset
передается обратно в render/3
.
Я обновил свое решение. Я читал, что apply_action — это способ добавить действие в набор изменений. Я передаю набор изменений обратно. но поскольку новый набор изменений не имеет значений, выбранных из флажка, поскольку они были частью ассоциации.
А, понял. Вы все еще можете применять ассоциации, я думаю; просто избегайте вызова Repo.insert/2
для недопустимого набора изменений.
применение ассоциации получило ту же ошибку argumentsError, что и раньше. изменения |> put_parking_association(attrs["parkings"]) |> apply_action(:insert) Это дает мне тот же набор изменений, который я разместил в начале вопроса, что вызывает аргументError. В нем говорится, что Html не может отображать список Ecto.changeset. получена неверная запись: #Ecto.Changeset<action: :update, изменения: %{}, ошибки: [], данные: #Tailwind.Parkings.Parking<>, действительно?: true>
Мне удалось сохранить проверенные значения, используя виртуальные поля. Спасибо за солнышко.
Я вижу, когда набор изменений недействителен, нет смысла добавлять ассоциацию. В вашей функции handle_error я вернул набор изменений |> apply_action(:insert), и это сработало. Но есть одна проблема, все информационные флажки, которые я проверил, потеряны. Есть ли способ сохранить информацию о флажке и показать ее с другими ошибками проверки? Я не мог вывести (или понять) какую-либо полезную информацию из документов ecto.