Fix decimal load from JSON serialized map#3215
Conversation
|
So this fix may solve different use cases, but isn't the proper solution here to: - field :settings, :map, default: %{}
+ embeds_one :settings, MySettings? then it all works correctly. below is a script that I used to test this out. Details |
|
@wojtekmach In my use case there are a few different settings schemas and I need to dynamically load the correct one from the backing JSON column |
|
we solved this for decimal but the same problem exists for e.g. |
|
I guess it should work for all ecto primitive types This includes basic types, structs (Decimal, Time, Date etc), arrays, maps and nested schemas |
|
@lukaszsamson i am working on something that will make your life a bit easier. Give me 10. |
|
So the issue in your approach is that you are assuming that the data encoded/decoded for embedding is the same as the data encoded/decoded for JSON. Unfortunately that's not true and All database adapters therefore have to write a encoding/decoding layer, which I have ported to Ecto as Ecto.Type.embedded_load and Ecto.Type.embedded_dump. Before encoding the value to JSON, you should call Ecto.Type.embedded_dump for each field and value. After decoding the value from JSON, you should call Ecto.Type.embedded_load for each field and value. That said, it is best if your |
|
I can give it a try. |
|
@josevalim I managed to refactor my app to use a custom ecto type as you suggested. Here's the code I ended up with. I have to say that is's a lot more complex than the previous version. The obvious advantage though is that loading/dumping now works automatically and the data is more strongly typed. The downside is that i had to add a marker field to the serialized JSON as there is no context available in |
|
Yes, the marker field is essential, otherwise you will have strings on decimal fields, encrypted data won't be handled, etc. The only change I would do in your code is to make the type field a mapping. Something like: @types %{
"video" => MyApp.Video,
"audio" => MyApp.Audio
}This way you don't couple the data in the database with your application names and you can skip the whole Code.ensure_loaded? dance, as you know these schemas exist. |
|
The coupling would still be there, just less explicit :) |
|
Not really! The coupling I was pointing to was the need to change the data in your database in case you rename a module in Elixir, which is definitely not a good idea. By introducing a mapping, The database doesn’t keep any knowledge of your application. |
Given schemas
storing & loading
MySettingsin Postgres JSON columnresults in