Reputation: 1930
I'm working on creating a local db schema for a remote API, and I have some problems.
# CatalogObject
schema "catalog_objects" do
field :type, :string
has_one :item_data, ItemData, foreign_key: :catalog_object_id
has_one :item_variation_data, ItemVariationData, foreign_key: :catalog_object_id
end
# ItemData
schema "item_data" do
field :name, :string
...
belongs_to :catalog_object, CatalogObject
end
# ItemVariationData
schema "item_variation_data" do
field :name, :string
...
belongs_to :catalog_object, CatalogObject
end
# API Response
%{
"id" => "UA3XRLH75LEFRXCF2YWEUKB",
"item_data" => %{
"name" => "NY Steak",
"variations" => [
%{
"id" => "2DYQSRC4AUZGAMCFTCOD24Q",
"item_variation_data" => %{
"item_id" => "UA3XRLH75LEFRXCF2YWEUKB",
],
"name" => "Regular",
},
"type" => "ITEM_VARIATION",
}
],
},
"type" => "ITEM",
}
I'm able to create item_data
based on the type
being ITEM without any problem with create_catalog_object/1
, but I don't know where to start with the data in variations
since they are supposed to be casted as CatalogObject
with type
being ITEM_VARIATION.
I would like to create Item CatalogObject with ItemData and ItemVariation CatalogObject with ItemVariationData in one go, but I couldn't use put_assoc
or cast_assoc
in ItemData.changeset
since ItemData
is not related to ItemVariation CatalogObject
Is there a way to create all the records in one go?
Sorry if I didn't explain the problem correctly.
Upvotes: 0
Views: 73
Reputation: 1160
After giving some thought about this, would this work for you?
defmodule ItemData do
use Ecto.Schema
import Ecto.Changeset
schema "item_data" do
field :name, :string
belongs_to :catalog_object, CatalogObject
has_many :variations, CatalogObject
end
def changeset(item_data, params \\ %{}) do
item_data
|> cast(params, [:name])
|> put_assoc(:variations, Map.get(params, :variations, []))
end
end
defmodule ItemVariationData do
use Ecto.Schema
import Ecto.Changeset
schema "item_variation_data" do
field :name, :string
belongs_to :catalog_object, CatalogObject
end
def changeset(item_data, params \\ %{}) do
item_data
|> cast(params, [:name])
end
end
defmodule CatalogObject do
use Ecto.Schema
import Ecto.Changeset
schema "catalog_objects" do
field :type, :string
# The on_replace option tells Ecto that it is okay to update these fields
# with a put_assoc or cast_assoc in a changeset.
has_one :item_data, ItemData, on_replace: :update
has_one :item_variation_data, ItemVariationData, on_replace: :update
end
def changeset(item_data, params \\ %{}) do
item_data
|> cast(params, [:type])
|> put_assoc(:item_data, Map.get(params, :item_data))
|> put_assoc(:item_variation_data, Map.get(params, :item_variation_data))
end
end
# Example for how to create a CatalogObject struct from your API response
catalog_object = %CatalogObject{
type: "ITEM",
item_data: %ItemData{
name: "NY Steak",
variations: [
%CatalogObject{
type: "ITEM_VARIATION",
item_variation_data: %ItemVariationData{
name: "Regular"
}
}
]
}
}
# Wherever you want to create the CatalogObject
catalog_object
|> CatalogObject.changeset()
|> Repo.insert()
Upvotes: 1