How to create a product with a category id?

The setup: lib/app/shop/resources/category.ex
defmodule App.Shop.Category do
use Ash.Resource, data_layer: Ash.DataLayer.Ets

attributes do
uuid_primary_key :id
attribute :name, :string
end

actions do
defaults [:create, :read]
end

code_interface do
define_for App.Shop
define :create
define :read
end
end
defmodule App.Shop.Category do
use Ash.Resource, data_layer: Ash.DataLayer.Ets

attributes do
uuid_primary_key :id
attribute :name, :string
end

actions do
defaults [:create, :read]
end

code_interface do
define_for App.Shop
define :create
define :read
end
end
lib/app/shop/resources/product.ex
defmodule App.Shop.Product do
use Ash.Resource, data_layer: Ash.DataLayer.Ets

attributes do
uuid_primary_key :id
attribute :name, :string
attribute :price, :decimal
end

relationships do
belongs_to :category, App.Shop.Category
end

actions do
defaults [:create, :read]
end

code_interface do
define_for App.Shop
define :create
define :read
end
end
defmodule App.Shop.Product do
use Ash.Resource, data_layer: Ash.DataLayer.Ets

attributes do
uuid_primary_key :id
attribute :name, :string
attribute :price, :decimal
end

relationships do
belongs_to :category, App.Shop.Category
end

actions do
defaults [:create, :read]
end

code_interface do
define_for App.Shop
define :create
define :read
end
end
Question: How can I create a new Product which includes a Category? This is what I tried:
iex(1)> fruits = App.Shop.Category.create!(%{name: "Fruits"})
#App.Shop.Category<
__meta__: #Ecto.Schema.Metadata<:loaded>,
id: "5b582e8a-3c19-4ce4-b609-88748a46e2ce",
name: "Fruits",
aggregates: %{},
calculations: %{},
...
>
iex(3)> orange = App.Shop.Product.create!(%{name: "Orange", price: 0.20, category: fruits})
#App.Shop.Product<
category: #Ash.NotLoaded<:relationship>,
__meta__: #Ecto.Schema.Metadata<:loaded>,
id: "432152d7-715d-4731-8fe4-ed90b05ee30e",
name: "Orange",
price: Decimal.new("0.2"),
category_id: nil,
aggregates: %{},
calculations: %{},
...
>
iex(4)> orange = App.Shop.Product.create!(%{name: "Orange", price: 0.20, category_id: fruits.id})
#App.Shop.Product<
category: #Ash.NotLoaded<:relationship>,
__meta__: #Ecto.Schema.Metadata<:loaded>,
id: "68c1023e-5b57-4819-a4a2-ff16f7efdc5d",
name: "Orange",
price: Decimal.new("0.2"),
category_id: nil,
aggregates: %{},
calculations: %{},
...
>
iex(5)>
iex(1)> fruits = App.Shop.Category.create!(%{name: "Fruits"})
#App.Shop.Category<
__meta__: #Ecto.Schema.Metadata<:loaded>,
id: "5b582e8a-3c19-4ce4-b609-88748a46e2ce",
name: "Fruits",
aggregates: %{},
calculations: %{},
...
>
iex(3)> orange = App.Shop.Product.create!(%{name: "Orange", price: 0.20, category: fruits})
#App.Shop.Product<
category: #Ash.NotLoaded<:relationship>,
__meta__: #Ecto.Schema.Metadata<:loaded>,
id: "432152d7-715d-4731-8fe4-ed90b05ee30e",
name: "Orange",
price: Decimal.new("0.2"),
category_id: nil,
aggregates: %{},
calculations: %{},
...
>
iex(4)> orange = App.Shop.Product.create!(%{name: "Orange", price: 0.20, category_id: fruits.id})
#App.Shop.Product<
category: #Ash.NotLoaded<:relationship>,
__meta__: #Ecto.Schema.Metadata<:loaded>,
id: "68c1023e-5b57-4819-a4a2-ff16f7efdc5d",
name: "Orange",
price: Decimal.new("0.2"),
category_id: nil,
aggregates: %{},
calculations: %{},
...
>
iex(5)>
The category_id is nil both times.
9 Replies
kernel
kernel2y ago
use manage_relationship
actions do
create :create do
argument :category_id, :uuid
change manage_relationship(:category_id, :category, type: :append_and_remove)
end
end
actions do
create :create do
argument :category_id, :uuid
change manage_relationship(:category_id, :category, type: :append_and_remove)
end
end
roughly something like that ( i think you may have to swap the first two params in the manage_relationship)
kernel
kernel2y ago
or directly on the changeset
No description
barnabasj
barnabasj2y ago
If you wan't to set the id directly you need to make the attribute writeable https://ash-hq.org/docs/dsl/ash-resource#relationships-belongs_to-attribute_writable-
Ash HQ
Ash.Resource
View the documentation for Ash.Resource on Ash HQ.
Stefan Wintermeyer
What is the right syntax for that? https://ash-hq.org/docs/dsl/ash-resource#relationships-belongs_to-attribute_writable- doesn't contain an example. This doesn't work:
relationships do
belongs_to :category, App.Shop.Category do
writeable?: true
end
end
relationships do
belongs_to :category, App.Shop.Category do
writeable?: true
end
end
barnabasj
barnabasj2y ago
I think it should be attribute_writable? not just writeable? inside the do\end block you never have to use :
relationships do
belongs_to :category, App.Shop.Category do
attribute_writeable? true
end
end
relationships do
belongs_to :category, App.Shop.Category do
attribute_writeable? true
end
end
Stefan Wintermeyer
Thanks @barnabasj! For the archive. Here's the fixed code and how it's working:
relationships do
belongs_to :category, App.Shop.Category do
attribute_writable? true
end
end
relationships do
belongs_to :category, App.Shop.Category do
attribute_writable? true
end
end
iex(13)> orange = App.Shop.Product.create!(%{name: "Orange", price: 0.20, category_id: fruits.id})
#App.Shop.Product<
category: #Ash.NotLoaded<:relationship>,
__meta__: #Ecto.Schema.Metadata<:loaded>,
id: "40cc21c2-58f8-4db4-8d98-7ab7c1741fa8",
name: "Orange",
price: Decimal.new("0.2"),
category_id: "5b582e8a-3c19-4ce4-b609-88748a46e2ce",
aggregates: %{},
calculations: %{},
...
>
iex(13)> orange = App.Shop.Product.create!(%{name: "Orange", price: 0.20, category_id: fruits.id})
#App.Shop.Product<
category: #Ash.NotLoaded<:relationship>,
__meta__: #Ecto.Schema.Metadata<:loaded>,
id: "40cc21c2-58f8-4db4-8d98-7ab7c1741fa8",
name: "Orange",
price: Decimal.new("0.2"),
category_id: "5b582e8a-3c19-4ce4-b609-88748a46e2ce",
aggregates: %{},
calculations: %{},
...
>
This doesn't work:
App.Shop.Product.create!(%{name: "Orange", price: 0.20, category: fruits})
App.Shop.Product.create!(%{name: "Orange", price: 0.20, category: fruits})
Should it but I configured the resource wrong or shouldn't it never because this isn't ActiveRecord?
barnabasj
barnabasj2y ago
No it shouldn't work out of the box. If you wan't that you need to use manage_relationship like kernel suggested
Stefan Wintermeyer
@barnabasj Since I am writing beginners documentation I'd like to know if I understand it right: Code like App.Shop.Product.create!(%{name: "Orange", price: 0.20, category: %{name: "Fruits"}} is not the Ash way or is it? I am not so much interested in what is possible by opening all escape hatches but what is the intended way of doing it. I have one new Category and I have one new Product. What is the fastest way of creating it? What is the Ash way of doing it?
barnabasj
barnabasj2y ago
I think both ways are equally valid and Ash just doesn't assume what you want. You have to be more explicit by telling ash how you would like to manage your relationships I use both ways in my code. Depending on the life-cycle of the resources and which resource I want to manage the relationship and so on.

Did you find this page helpful?