I’m building a File Manager with a tagging system. I have a FileManagement context in a Phoenix application with File and Tag in a many to many association through FileTag.
When creating a File I always want at least 1 Tag associated to it, so I’ve implemented a get_or_insert_tag() function inside the File schema which currently looks like this:
# file_management/file.ex
schema "files" do
field :file_size, :integer
field :file_type, :string
field :name, :string
field :url, :string
many_to_many :tags, Tag, join_through: FileTag
timestamps()
end
@doc false
def changeset(%File{} = file, attrs) do
file
|> cast(attrs, [:name, :url, :file_type, :file_size])
|> validate_required([:name, :url, :file_type, :file_size])
|> put_assoc(:tags, get_or_insert_tag(attrs), required: true)
end
defp get_or_insert_tag(%{tags: [name]}) do
%Tag{name: name}
|> Repo.insert(
on_conflict: [set: [name: name]],
conflict_target: :name
)
end
I based it on this: Many to many and upserts « Plataformatec Blog article, but I’m having some issues adapting it to this use case. When using the create_file/1 function and passing it a file like the following:
%{
name: "nEEDza1",
file_size: 743686,
file_type: "jpg",
url: "https://s3-us-west-2.amazonaws.com/bucket-name-here/nEEDza1.jpg",
download_count: 0,
tags: [
"etc"
],
}
I get an error like this:
** (MatchError) no match of right hand side value: {:error, ecto.Changeset<action: :insert, changes: %{file_size: 943916, file_type: “png”, name: “5Sw5ZtG.png”, url: “https://s3-us-west-2.amazonaws.com/bucket-name-here/5Sw5ZtG.png”}, errors: [tags: {“is invalid”, [type: {:array, :map}]}], data: #Filix.FileManagement.File<>, valid?: false>}
The Tag is successfully being upserted, but the File isn’t liking the Tag it should be associated to. I get the feeling this is something simple that I’m missing - I’m not very familiar with Ecto yet, so any help would be appreciated.