lusketeer
lusketeer

Reputation: 1930

Create non associated records with changeset

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

Answers (1)

Peter
Peter

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

Related Questions