Hello, as the title says, I have a table that belongs to another one, this table (The association) has a column which is a list, what I want to do is append data to that list whenever a command is executed, the first time it works, I can append the first element to the list but after that it throws me an error saying that the type is invalid, that it is a map. (I’m using Postgres if that matters)
Error reason on censor command: #Ecto.Changeset<action: :update, changes: %{},
errors: [censored_words: {"is invalid", [type: {:array, :map}]}],
data: #Censorex.DB.Guild<>, valid?: false>
My concern here is, why does it accept it as a map the first time, converts it to the type that the field is but when I try it a second time it complains? I mean, I can see that I’m passing a map when I should pass a list. But how can I pass the information to the changeset if that is causing the error then?
I’ve tried several “solutions” and I ended up with a not too good looking code for this task. Here’s piece that makes that task.
def censor(msg) do
[_| tail] = String.split(msg.content, " ")
censored = %{
id: get_censor_id(Helpers.get_guild_id(msg.channel_id)),
words: [Enum.join(tail, " ")]
}
IO.inspect censored, label: "Censored var"
message =
with guild <- Repo.get_by(Guild, guild: Helpers.get_guild_id(msg.channel_id))
|> Repo.preload(:censored_words),
changeset <- Changeset.change(guild)
|> Changeset.put_assoc(:censored_words, flatten_words(guild) ++ [censored]),
{:ok, _struct} <- Repo.update(changeset) do
"**New command added**"
else
{:error, reason} ->
IO.inspect(reason, label: "Error reason on censor command")
"**There was an error trying to register your command**"
_ -> ":x:"
end
Api.create_message(msg.channel_id, message)
end
# Priv
# This function is here because I was getting the list elements as a 2D list and when I tried to
# append it, I ended up with a weird looking list.
defp flatten_words(list) do
list.censored_words
|> Enum.map(&(&1.words))
|> List.flatten
|> IO.inspect(label: "Flatten words lists")
end
# And this one is because I was getting an error saying that I needed to pass the id of the
# association to the map above (censored variable)
defp get_censor_id(guild_id) do
guild =
Guild
|> Repo.get_by(guild: guild_id)
|> Repo.preload(:censored_words)
guild.censored_words
|> Enum.map(&(&1.id))
|> List.first
|> IO.inspect(label: "Censor id")
end
I don’t know if you need to see the schema for this but here they are just in case:
guilds.ex
defmodule Censorex.DB.Guild do
@moduledoc false
use Ecto.Schema
import Ecto.Changeset
alias Censorex.DB
schema "guild" do
field :owner, :integer
field :guild, :integer
has_many :censored_words, DB.Censored
timestamps()
end
def changeset(model, params \\ %{}) do
model
|> cast(params, [:owner, :guild])
|> unique_constraint(:guild)
end
end
censored.ex
defmodule Censorex.DB.Censored do
@moduledoc false
use Ecto.Schema
alias Censorex.DB
schema "censored" do
field :words, {:array, :string}, default: []
field :guild_id, :integer
belongs_to :guild, DB.Guild, define_field: false
timestamps()
end
end
Thanks for any help ^-^