PaleWatcher
I’m trying to model something in Ecto where records share a few common fields, but depending on a type, they have completely different additional fields.
Roughly:
- there’s an
itemstable with the common stuff (type,same_field, etc.) - and then separate tables for each type (
items_a,items_b) - these subtype tables use the same
idas the parent (so 1:1, PK = FK)
So everything is still one logical collection, just split across tables to avoid a lot of nullable columns and to keep proper DB constraints.
In Ecto I’m using has_one + cast_assoc, and dispatching based on the type.
I’m wondering:
- does this approach fit well with Ecto, or is it something people generally avoid?
- is there a more idiomatic way to solve this without collapsing everything into one table or using JSON?
- are there any gotchas with this setup (especially around inserts or preloading)?
Example changeset:
def changeset(item, attrs) do
item
|> cast(attrs, [:type, :category, :owner_id])
|> validate_required([:type, :category, :owner_id])
|> cast_subtype()
end
The whole idea is to keep validation at the database level as much as possible, not just in changesets.
Trending in Questions
I’m working on a project that simulates the bumbl example in the programming phoenix book. It acts almost like an email client. We have a...
New
Hello!
Could someone please give me a help/sample code, how to delete a file from s3 using waffle/waffle_ecto from Phoenix app.
I creat...
New
I’m seeing that a list inside a Kino.DataTable will be interpreted as a charlist, even if the Kino.configure() is set to charlists: :as_l...
New
So my question is quite simple and i have found no conclusive answer on forum, google or AI.
Should we use :erlang.float for Integer to ...
New
Hi, I’ve just set up an application with ash_authentication. There is only magic link strategy for now, so there is no confirmation add o...
New
If a change or preparation module uses Ash.Changeset.get_argument/2 or Ash.Query.get_argument/2 (or any of the other get_argument functio...
New
Hello,
I know there is an approach for handling lists that allows for optimized traversal, but I can’t recall the specific method (somet...
New
Other Trending Topics
I am happy to introduce the very α version of the new programming language compiled to BEAM.
Welcome Cure.
It has literally three kille...
New
Hi there! We created Gust: A task orchestrator inspired by Airflow.
For those who have never heard about Aiflow, it’s a Python-based wor...
New
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Xamal is a deployment tool for Elixir apps that deploys native releases to bare metal servers over SSH. It’s a port of GitHub - basecamp/...
New
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge & Solve.
They are GUI (Emerge) and State management (S...
New
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #library
- #deployment
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #elixirconf
- #channels
- #exunit
- #discussion
- #code-sync
- #podcasts
- #javascript
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixirconf-us
- #blog-post
- #elixir-ls
- #ai
- #phoenix_html
- #iex
- #graphql
- #genstage
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #hex
- #security
- #metaprogramming










Showing Posts 1 to 10- Show Best Posts
- Show All (oldest first)
- Show All (newest first)
sodapopcan
I’m slightly confused by your wording…
items_aanditems_b, the plural makes me think it’s ahas_many, but you then you saidhash_oneso I’m assuming that. SO, I actually have a mostly working on a library to handle this more gracefully as we had a few tables like that at my old work. But since I stopped working there I lost motivation. I could post what I have, though. The API is basically:It basically adds sister versions of a few functions with
_oneadded (kinda like_embedsversions), socast_one,put_one_)assoc, andget_one_assoc(for each you can refer to them by the first argument to theonemacro). There is alsoMyRepo.preload_one(which is a stupid name and I think that function is unfinished for nested preloads). I can post what I have if you’re interested.Otherwise, as far as preloading goes, you can do this and it’s efficient:
When preloading it will simply ignore the associations whose ids are
nil. I would definitely hide this in a function, though, as they get unweildly if you are preloading a lot.PaleWatcher
Yeah, good point about the naming — that’s on me. it’s
has_onein practice.Your
one :itemabstraction is actually very close to what I’m doing manually.I’d definitely be interested in seeing it, especially how you handled:
Also curious: did you end up using this pattern long-term, or did you move away from it?
I’m definitely interested, mostly from a learning perspective.
This approach feels “right” to me so far — especially compared to pushing everything into a JSON field. Even if it means a few extra tables and joins, I like that it keeps things well-structured and avoids nullable fields.
I should also mention that I haven’t really worked with macros yet, so seeing how you approached that part would be especially interesting.
On the UI side, I’ve also tried modeling this in LiveView forms using function components for each subtype, so each type has its own set of inputs. It works, but after a while it starts to feel a bit cumbersome to maintain.
sodapopcan
Sure thing! I’ll clean it up and push it a bit later and let you know.
Note I made a mistake in the API I showed which I’ve edited. I just forgot the
belongs_tos so theonemacro is doesn’t do anything too magical. It’s has a lot in common with howtestworks which is outlined in the official guides.And yes, preloading gets hairy if they are deeply nested, but it’s just a wrapper around
Repo.preloadthat looks for:itemand replaces it with the proper preload. Of course, it’s not that simple because it actually has to reflect on the schema to ensure that:itemin a preload really does mean theone :itemsince it could refer to something else in a nested preload. That’s where it gets messy.I’m sure my old team has not moved from this pattern (I only left a few weeks ago, lol) and ya, I don’t think there is anything wrong with it. Ecto very specifically does not support polymorphic associations with a
typecolumn.On speaking of that, my lib allows for adding either a virtual or concrete
typecolumn which can come in handy in pattern matching and doingallqueries for a specific type respectively.I’ll let you know when I’ve posted it.
LostKobrakai
I recently read a good blog post on this:
This is about the plain db level. Once you have that you can add the schemas for that.
You likely want to have another layer of this eventually, which can map between
[ItemA, ItemB]to those lower level nested schemas.sodapopcan
I really like this, thanks for sharing. It’s quite clever and a pretty clean solution.
sodapopcan
As promised here is the repo. I’m a bit embarrassed by the
preload_oneas I was in the middle of working on it (this was made with 0 vibes) but I keep hopping around on projects and I need to stop doing that so here ya go!I think the approach linked by LK is a much more interesting way of doing this, although the same Ecto API could still apply.
Also the README is not fleshed out at all but the all the docstrings are.
pjode
This is a non-answer but something I learned to do early on in a project is not even worry about it and just have all the columns on the one table and then document the schema which fields belong to which subtype and then have separate changesets for creating the different subtypes.
This is usually good enough until a better understanding of requirements show themselves.
sodapopcan
This is the strategy my library was trying to solve. We did just this at my last job but it ballooned very quickly. EDIT: By “solve” I mean make easier, but same idea: it’s when you put all the sub types right on the parent table and only allow one of them to be present.
Schultzer
Whats easy in SQL are hard in Ecto.
Thats why I’m building GitHub - elixir-dbvisor/sql: SQL provides state-of-the-art, high-performance SQL integration for Elixir, built to handle extreme concurrency with unmatched expressiveness and ergonomic query composition. Write safe, composable, parameterized queries directly, without translating to Ecto or any ORM. · GitHub to make it easy both places.
sodapopcan
Oh ya, I keep meaning to check this out. It looks really nice I just haven’t played with it yet as there are a bunch of things I wanna do (and someone recently starting to use AI a bit has made that worse). I’ve also been heavy into an Ash project and I’m ass-u-me’ing it wouldn’t make much sense to use in Ash?