I want a virtual assoc, so that I can do polymorphic assocs. So I have a picture schema, that is related to Users (and others). I will have a place in the callback where I assign the polymorphic type/id to the schema, but in order to do so, it needs to be assigned to the changes, but with this I'm struggled. Currently I have.
defmodule App.Picture do
schema "pictures" do
...
field :pictureable, :map, virtual: true
end
@attrs [:picture, :pictureable_id, :pictureable_type]
def changeset(picture, attrs) do
picture
|> cast(attrs, @attrs ++ [:pictureable])
|> handle_polymorphic
|> cast_attachments(attrs, [:picture])
|> validate_required(@attrs)
end
def handle_polymorphic(%{changes: %{pictureable: %type{id: id}}} = ch) do
ch
|> put_change(:pictureable_type, type)
|> put_change(:pictureable_id, id)
end
end
Fairly straightforward. You create a picture using attrs like %{picture: Path.to("/image"), pictureable: user}, which would be transformed into, %{picture: Arc.stuff, pictureable_id: 1, pictureable_type: "App.User"}
The issue with the code above is that :pictureable doesn't get kept in the changeset
pic = %Plug.Upload{content_type: ...}
user = insert(:user)
args = %{picture: img, pictureable: user}
ch = Picture.changeset(%Picture{}, args)
My changeset comes back like this
#Ecto.Changeset<
action: nil,
changes: %{
picture: %{
file_name: "10x10.png",
updated_at: #Ecto.DateTime<2018-05-05 18:59:18>
}
},
errors: [
pictureable_id: {"can't be blank", [validation: :required]},
pictureable_type: {"can't be blank", [validation: :required]},
pictureable: {"is invalid", [type: :map, validation: :cast]}
],
data: #App.Picture<>,
valid?: false
>
Why will it not assign the map as expected for the virtual schema?
EDIT: It works if I use field :pictureable, :any, virtual: true. So it seems it's some issue with a struct not being a map, even though the user would pass a is_map(user) test. Any idea why it's being treated like it's not a map?