DEV Community

Pau Riosa
Pau Riosa

Posted on

How does Ecto.Schema's `has_one/3` works?

The problem

I have two schemas, coffees and orders wherein I want to get the recent completed order from each coffees

First solution

defmodule Schemas.Coffee do # ... more code has_many :completed_orders, Schemas.Order def coffee_query(coffee_id) do from c in __MODULE__, where: c.id == coffee_id, preload: [completed_orders: Orders.completed_order], limit: 1 end end defmodule Schemas.Order do # ... more code @completed_status :completed def completed_order_query() do from o in __MODULE__, where: o.status in ^@completed_status, order_by: [desc: o.updated_at] end end defmodule Contexts.Coffees do # more code def get_recent_completed_coffee_order(coffee_id) do coffee = coffee_id |> Schemas.Coffee.coffee_query() |> Repo.one List.first(coffee.completed_orders) end end 
Enter fullscreen mode Exit fullscreen mode
  • Based from the setup, I can actually get the completed_orders of coffees but the only downside for me is that the preload will return a list

  • Since it will return a list , that will result into another process for me just to get the recent completed order.

Then, has_one/3 came into my life

defmodule Schemas.Coffee do # ... more code has_one :completed_order, Schemas.Order, where: [status: :completed], preload_order: [desc: :updated_at] def coffee_query(coffee_id) do from c in __MODULE__, where: c.id == ^coffee_id preload: [:completed_order], limit: 1 end end defmodule Contexts.Coffees do # more code def get_recent_completed_coffee_order(coffee_id) do coffee_id |> Schemas.Coffee.coffee_query() |> Repo.one |> Map.get(:completed_order, nil) end end 
Enter fullscreen mode Exit fullscreen mode

What's the magic behind this scheme?

How does has_one/3 works?

Indicates a one-to-one association with another schema

From the current situation, coffee belongs_to order. As a result, coffee has_many orders, but I only need one result to return coming from the orders schema to identify if the coffee order is completed already.

Using has\_one/3\ I was able to achieve what I want.

  • has_one/3 - indicates a one-to-one association with another schema.

  • where - indicates a Filtering associations to get my specific items.

  • preload_order - Sets the default order_by of the association, and since I am using has_one/3 Ecto set a query LIMIT set to one .

[see has_one/3 on docs](https://hexdocs.pm/ecto/Ecto.Schema.html#has_one/3)

Happy Coding

Top comments (0)