I seem to be stuck on some basic Ecto logic.
my Ecto version is 3.7.1
Given some schema:
schema :users do
field :name, string
field :zone, Ecto.Enum, values: ["Zone A": 1, "Zone B": 2, "Zone A and B": 3] # stored as integer in db
FYI: I don’t want to use composite types of
:string type for the zone.
Then when I do query the data:
# simplified for brevity
u in Users,
select: [:id, :name, :zone],
where: [name: ^some_name]
Each user object’s
zone is shown as atom:
name: "User A",
zone: :"Zone A" # I need this as integer
How do I make Ecto return the corresponding ids for
The reason I need that is later in the code I’m trying to create two list of users (with one db query), like so:
users = Users.get_all_active_users()
users_zone_a = Enum.reject(users, fn u -> u.zone == 2 end)
users_zone_b = Enum.reject(users, fn u -> u.zone == 1 end)
Any help greatly appreciated
That’s the idea of the enum in this case, to separate the presented value from the persisted one. What I would advise is to create a helper module which can encode and decode integers to and from
:"Zone B" and so on. You should do the encoding/decoding at the “points” where your code interacts with the external word e.g. HTTP endpoints, database. This would allow you to work only with meaningful values and your comparisons will work.
I realised (during breakfast…) that I could filter by the atom instead
Enum.reject(users, fn u ->
u.zone == :"Zone A"
Not ideal but it works. Probably would be wise to have some encode/decode helper for those so it’s not spread all over the codebase.
The whole idea of
Ecto.Enum is to not spread the persisted value (the integers in your case) all over the codebase. You’re expected to use the atoms wherever you interact with the field, not the integers. Otherwise you could just use an
:integer field to begin with.
But also there’s
Ecto.Enum.mappings if you want to map between the representations.