Files
livebook/ash_tutorial/relationships.livemd
2025-09-29 19:25:33 +08:00

164 lines
4.4 KiB
Markdown

# Ash: 6 - Relationships
```elixir
Application.put_env(:ash, :validate_domain_resource_inclusion?, false)
Application.put_env(:ash, :validate_domain_config_inclusion?, false)
Mix.install([{:ash, "~> 3.0"}], consolidate_protocols: false)
```
## Relationships
<div class="flex items-center w-full flex-start justify-between rounded-xl p-4" style="background-color: #f0f5f9; color: #61758a;">
<div class="flex">
<i class="ri-arrow-left-fill"></i>
<a class="flex ml-2" style="color: #61758a;" href="customizing_actions.livemd">Customizing Actions</a>
</div>
<div class="flex">
<i class="ri-home-fill"></i>
<a class="flex ml-2" style="color: #61758a;" href="overview.livemd">Home</a>
</div>
<div class="flex">
<a class="flex mr-2" style="color: #61758a;" href="code_interfaces.livemd">Code Interfaces</a>
<i class="ri-arrow-right-fill"></i>
</div>
</div>
### In this tutorial you will create a relationship between a Ticket and Representative resource
Create a `relationships do .. end` block in the Ticket resource.
Inside the `relationships` block, define a `belongs_to` representative like so:
`belongs_to :representative, Tutorial.Support.Representative`
<details class="rounded-lg my-4" style="background-color: #96ef86; color: #040604;">
<summary class="cursor-pointer font-bold p-4"></i>Show Solution</summary>
<div class="p-4">
```elixir
defmodule Tutorial.Support.Ticket do
# ...
relationships do
# belongs_to means that the destination attribute is unique, meaning only one related record could exist.
# We assume that the destination attribute is `representative_id` based
# on the name of this relationship and that the source attribute is `representative_id`.
# We create `representative_id` automatically.
belongs_to :representative, Tutorial.Support.Representative
end
# ...
```
</div>
</details>
### Enter your solution
```elixir
defmodule Tutorial.Support.Ticket do
use Ash.Resource,
domain: Tutorial.Support,
data_layer: Ash.DataLayer.Ets
actions do
defaults [:read]
create :create do
accept [:subject, :description, :status]
end
end
attributes do
uuid_primary_key :id
attribute :subject, :string, allow_nil?: false
attribute :description, :string
attribute :status, :atom do
constraints one_of: [:open, :closed]
default :open
allow_nil? false
end
create_timestamp :created_at
update_timestamp :updated_at
end
# <-- Add the relationship here
end
defmodule Tutorial.Support.Representative do
use Ash.Resource,
domain: Tutorial.Support,
data_layer: Ash.DataLayer.Ets
actions do
defaults [:read]
create :create do
accept [:name]
end
end
attributes do
uuid_primary_key(:id)
attribute(:name, :string)
end
end
```
```elixir
defmodule Tutorial.Support do
use Ash.Domain
resources do
resource Tutorial.Support.Ticket
resource Tutorial.Support.Representative
end
end
```
## Creating a Ticket record with a representative Relationship
First, create the representative Joe.
```elixir
# Creates a Representative
joe =
Tutorial.Support.Representative
|> Ash.Changeset.for_create(:create, %{name: "Joe Armstrong"})
|> Ash.create!()
```
Next up, create a ticket and assign the representative Joe to the ticket.
```elixir
# Creates a Ticket with the representative
Tutorial.Support.Ticket
|> Ash.Changeset.for_create(:create, %{subject: "My spoon is too big!", representative_id: joe.id})
|> Ash.create!()
# `load!/2` loads in the representative. Try removing this line to see what changes
|> Ash.load!([:representative])
```
As you can see it didn't quite work. The `representative_id` is not accepted by the create action on `Tutorial.Support.Ticket`. If you add `:representative_id` to that list,
and try again you should now see that the representative was correctly assigned.
<!-- livebook:{"break_markdown":true} -->
<div class="flex items-center w-full flex-start justify-between rounded-xl p-4" style="background-color: #f0f5f9; color: #61758a;">
<div class="flex">
<i class="ri-arrow-left-fill"></i>
<a class="flex ml-2" style="color: #61758a;" href="customizing_actions.livemd">Customizing Actions</a>
</div>
<div class="flex">
<i class="ri-home-fill"></i>
<a class="flex ml-2" style="color: #61758a;" href="overview.livemd">Home</a>
</div>
<div class="flex">
<a class="flex mr-2" style="color: #61758a;" href="code_interfaces.livemd">Code Interfaces</a>
<i class="ri-arrow-right-fill"></i>
</div>
</div>