Reputation: 2266
I have a changeset that looks like this:
%{
creator_id: "4",
name: "a test with GraphQL",
place_id: "13",
entree_id: "8",
base: "wheat",
type: "food"
}
My ecto schema is as follows:
schema "items" do
field :type, :string
field :name, :string
field :data, :map
belongs_to :creator, Name.SubName.Creators
belongs_to :place, Name.SubName.Places
belongs_to :entree, Name.SubName.Entrees
timestamps()
end
As you can see, base
is not a field that is in the ecto schema, I want to cast base
into the data
field so that it is a map
type, ie: {"base":"wheat"}
. I am using some form a single table inheritance here.
I am trying to add a record to my items
table. Right now when I read from the table, I cast the data
field to an embedded schema (depending on the type
) that contains my base
field just fine. Where I struggle is getting this field back into the database.
I tried using something like Ecto.Changeset.cast(params[:base], Map.keys(%{data: :map}))
but obviously that just gets the value which is clearly not a valid map. Additionally this would be painful to specify for all the unique fields that are associated with my other embedded schemas that represent each type
.
embeds_one
and embeds_many
do not apply to this situation because the data
ecto field represents different embedded schema types. So in this case my question is less about embedded schema use and more about picking out particular fields in a changeset
and turning them into a map
. Half of me just thinks I should make a distinct table for each type
and then I don't have to worry about this.
Upvotes: 0
Views: 2198
Reputation: 75740
You can use Map.split/2
:
@required_fields [:name, :type, :creator_id, :place_id, :entree_id]
@all_fields [:data | @required_fields]
def changeset(struct, params) do
{allowed, others} = Map.split(params, @required_fields)
params = Map.put(allowed, :data, others)
struct
|> cast(params, @all_fields)
|> # ...
end
Important thing to note here is that this example only works for maps with atom keys, you'll have to modify it a bit to work with both string and atom keys. You might also consider specifying an empty map as the default value for :data
.
Upvotes: 2