Decoratex, load data into Ecto virtual fields

Hi everyone,

Decoratex is my first Elixir package. It’s a very simple package but it helped me and my team to organize some logic of our project.

It provides methods to add virtual fields in Ecto models and it loads data into them when needed, keeping the Ecto model structure. It’s meant to be used for fields that can’t be calculated with just DB queries.

In this article, we explain our motivation in depth.

Here is an example of use:

defmodule User do
  use Ecto.Schema
  use Decoratex

  decorations do
    decorate_field :ability, :string, &UserHelper.get_ability/1
    decorate_field :absences, :integer, &UserHelper.count_absences/1

  schema "users" do
    has_many :periods, Period, on_delete: :delete_all
    belongs_to :setting, Setting
    field :email, :string

Then, you can load data into fields with decorate:

user = User 
|> Repo.get(1) 
|> Repo.preload(:settings) 
|> Repo.preload(:periods)
|> User.decorate
%User{email: …, ability: :manager, absences: 10}
# or User.decorate(:ability)
# or User.decorate([:absences, :ability])

And use the %User{} struct with extra data as you wish, for example in patttern matching, views, json…

As my frist package, I will appreciate any comments or suggestions :slight_smile:

In upcoming versions, I will try to add some improvements like sending custom params to the decoration functions.

Thank you!


If I put as decoration some data from a database, for example, some complex join query. Is there possibility to preload it for all elements, something like eager loading not to ping database every time when I call some decoration for any model in list for example?


Hi krapans,

I though on something like that, but I finally prefeered to keep all DB access outside the decorations so you can’t do a query in an unexpected place.

For example, we are trying to focus all the DB access in controllers, so we only use Repo there.
We have some modules to define queries where we do the eager loading and we use them only in controllers:

user = User
|> UserQuery.with_setting
|> UserQuery.with_periods
|> Repo.get
|> User.decorate(:ability)

And you can still decorating the user in other points when you need.

@attributes ~W(email ... absences)a

def render("show.json", %{user: user}) do
  render "show.json", user: User.decorate(user, :absences)

Also, we use pattern matching in the decorate functions to force us to use de appropriate queries in each case:

def get_ability(%User{setting: %Setting{} = setting} = user) do

So you must preload all needed data before any decoration.

I hope I understood your question, maybe you have another point of view or an idea about this so I will be glad to read about it :slight_smile:


Hi, I like the idea of this package! I’m trying to use it to use/display a virtual field, but I am getting an error. I think it is a problem with how I am passing the function to calculate the field in the decorate_field definition. What I am trying to do is have a display number that adds some letters and numeric padding to the id of the order record (i.e. order # 50 -> ABC00050). Here is my code:

defmodule Project.Order do
use Ecto.Schema
use Decoratex.Schema
import Ecto.Changeset

decorations do
decorate_field :order_number, :any, &(Order.assign_order_number/1)

schema “orders” do



def assign_order_number(%Order{} = order) do
%{order | order_number: “ABC” <> format_order_number(order.order_id)}

def format_order_number(order.order_id) do
|> Integer.to_string
|> String.pad_leading(5,“0”)

Hi @tom-gerken, sorry for the late response, I totally miss the forum notification :-(.

You should pass an anonymous function and I think that your definition is not exactly right, it should be




I hope you figured this out at time :sweat_smile:.

Thank you for use the package :-)!