zimt28
In my application I want to have a single entities table, which stores type a and type b entities. type a and type b share the exact same fields, I just set a type field can be "type_a" or "type_b".
For some reasons I don’t want to work with %Entity{} structs, but %TypeA{} and %TypeB{} structs, and I’m now wondering what might be the best way to do this.
In the end I’m looking for a way to create “aliases” for my %Entity{} struct. Here’s what I’ve tried so far:
1. Just define the schema multiple times
defmodule App.Entities.Entity do
# ...
schema "entities" do
field :title, :string
field :type, :string
has_one :address, Address
end
@doc false
def changeset(%Entity{} = entity, attrs) do
entity
|> cast(attrs, [:title, :type])
|> validate_required([:title, :type])
end
end
defmodule App.Entities.TypeA/B do
# ...
schema "entities" do
field :title, :string
field :type, :string
has_one :address, Address, foreign_key: :entity_id
end
@doc false
def changeset(%TypeA/B{} = entity, attrs) do
entity
|> cast(attrs, [:title, :type])
|> validate_required([:title, :type])
|> put_change(:type, "type_a/b")
end
end
This works fine, but I don’t like the repetitiveness of this approach.
2. Use a separate schema module
defmodule App.Entities.EntitySchema do
defmacro __using__(_) do
quote do
# ...
schema "entities" do
field :title, :string
field :type, :string
has_one :address, Address, foreign_key: :entity_id
end
end
end
end
defmodule App.Entities.Entity do
use App.Entities.EntitySchema
@doc false
def changeset(%Entity{} = entity, attrs) do
entity
|> cast(attrs, [:title, :type])
|> validate_required([:title, :type])
end
end
defmodule App.Entities.TypeA/B do
use App.Entities.EntitySchema
@doc false
def changeset(%TypeA/B{} = entity, attrs) do
entity
|> Map.put(:__struct__, Entity)
|> Entity.changeset(attrs)
|> put_change(:type, "type_a/b")
end
end
I prefer this approach, but it still seems to be a bit overkill for a “simple alias”.
3. Define structs
I have not tried this so far but it might be possible to use regular Elixir structs:
defmodule App.Entities.TypeA/B do
defstruct [:id, :title, :type, :address, :created_at, :updated_at]
end
defmodule App.Entities.Entity do
# ...
schema "entities" do
field :title, :string
field :type, :string
has_one :address, Address
end
@doc false
def changeset(%TypeA/B{} = entity, attrs) do
entity
|> Map.put(:__struct__, Entity)
|> changeset(attrs)
|> put_change(:type, "type_a/b")
end
def changeset(%Entity{} = entity, attrs) do
entity
|> cast(attrs, [:title, :type])
|> validate_required([:title, :type])
end
end
This seems to be a clean approach as my %TypeA/B{} structs are pure data containers,
but I’m not sure if I would lose some Ecto functionality, say preloads etc.?
Alternatives, Feedback?
How would you solve this problem and why? Maybe there’s a better approach that I haven’t thought of yet ![]()
Trending in Questions
Other Trending Topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #blog-post
- #ai
- #phoenix_html
- #iex
- #elixirconf-us
- #graphql
- #genstage
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #hex
- #security
- #metaprogramming










Showing Posts 1 to 1- Show Best Posts
- Show All (oldest first)
- Show All (newest first)
zimt28
I’m still not sure how I want to do it, but no. 3 – defining structs – is also possible:
I’ve moved this into a separate module so I can just do