I have a model that has a lot of Optional[] items, for maintainability the model is ordered alphabetically. However, for my particular use case I actually do care about the original order of the input dictionary, and it is almost never alphabetical (and it is not easily possible to pre-determine). Is it possible for Pydantic to preserve the original order of the input dictionary?
For now, I'm simply preserving the original dictionary (or if I care to save RAM, just a list of it's keys), but I suspect this might be helpful for more than just me, so it might be nice to be builtin to the Pydantic model?
Alternatively I could switch from a dictionary to a list, but due to how the rest of this program is structured it would be a breaking change, and one I'm not keen to make at this time.
For bugs/questions:
Where possible please include a self contained code snippet describing your
bug, question, or where applicable feature request:
from pydantic import BaseModel
test_dict = {
'three': 3,
'four': 4,
'two': 2,
'one': 1,
}
class Test(BaseModel):
one: int
two: int
three: int
four: int
test = Test(**test_dict)
print("Dictionary Order:")
for t in test_dict:
print(f"{t}: {test_dict[t]}")
print("\nPydantic Order:")
for t in test:
print(t)
print("\nOriginal Dictionary Order Workaround:")
for t in test_dict:
print(f"{t}: {getattr(test, t)}")
Output of the sample code:
Dictionary Order:
three: 3
four: 4
two: 2
one: 1
Pydantic Order:
('one', 1)
('two', 2)
('three', 3)
('four', 4)
Original Dictionary Order Workaround:
three: 3
four: 4
two: 2
one: 1
The short answer is "no", pydantic tries (with this caveat) to maintain the order from the model class definition, not the input data.
At some point I want to add whole model validators which could I guess be used to record the order of the original dict and even modify the model to switch the order.
Right now the best solution might be to mess with the order of the underlying __values__ of the model to alter order:
from pydantic import BaseModel
class Pet(BaseModel):
name: str
species: str
raw = dict(species='dog', name='foo')
p = Pet(**raw)
debug(p)
raw_order = {k: i for i, k in enumerate(raw.keys())}
ordered = dict(sorted(p.dict().items(), key=lambda kv: raw_order.get(kv[0], 1000)))
object.__setattr__(p, '__values__', ordered)
debug(p)
outputs:
test.py:12 <module>
p: <Pet name='foo' species='dog'> (Pet)
test.py:16 <module>
p: <Pet species='dog' name='foo'> (Pet)
closing this for tidiness, but feel free to ask more questions if you wish.
Thanks for the quick response! I think this will work for me for now.