According to the docs, Pydantic "ORM mode" (enabled with orm_mode = True
in Config
) is needed to enable the from_orm
method in order to create a model instance by reading attributes from another class instance. If ORM mode is not enabled, the from_orm
method raises an exception.
My questions are:
Fortunately, at least the first question can be answered fairly easily.
As of version 1.10.4
, there are only two places (aside from the plugins), where orm_mode
comes into play.
BaseModel.from_orm
This is basically an alternative constructor. It forgoes the regular __init__
method in favor of a marginally different setup. Not sure, why that is designed this way. But the orm_mode
flag must be set for this method to not raise an error. Straightforward. I see no hidden surprises here.
BaseModel.validate
This method is the default validator for the BaseModel
type. Without the orm_mode
flag, the validator expects a value that is either 1) an instance of that particular model, 2) a dictionary that can be unpacked into the constructor of that model, or 3) something that can be coerced to a dictionary, then to be unpacked into the constructor of that model.
If orm_mode
is True
and the validator encounters something that is not an instance of the model and not a dictionary, it assumes it is an object that can be passed to the aforementioned from_orm
method and calls that instead of trying the dict
coercion.
Note that this method is not called during initialization and it is not called if something is assigned to a model field of any type that isn't BaseModel
. It only comes into play, when you are dealing with nested models (and the objects that serve as the data input are also nested), i.e. with a model that has a field annotated with another model. Only then will the outer model call the validate
method of the inner model.
Consider the following:
from __future__ import annotations
from typing import TypeVar
from pydantic import BaseModel
M = TypeVar("M", bound=BaseModel)
class Foo(BaseModel):
x: int
@classmethod
def validate(cls: type[M], value: object) -> M:
print("called `Foo.validate`")
return super().validate(value)
class Config:
orm_mode = True
class A:
x = 1
foo = Foo.from_orm(A)
print(foo.json())
The output is {"x": 1}
and we see that Foo.validate
was not called.
Now we extend this a bit:
...
class Bar(BaseModel):
f: Foo
class Config:
orm_mode = True
class B:
f = A
bar = Bar.from_orm(B)
print(bar.json())
The new output:
called `Foo.validate`
{"f": {"x": 1}}
Now the validator was called as expected and if we were to inject a similar print
statement into Foo.from_orm
we would see that it too was called, when we called Bar.from_orm
right after Foo.validate
was called.
This may be relevant in certain niche situations, but generally speaking I would argue that this cascading application of from_orm
during validation makes sense and should accommodate the main intended use case -- database ORM objects.
If you want different behavior during validation, you can always define your own validator methods or even simply override the validate
method (depending on your use case).
There are no other uses of orm_mode
in the source code, so that is it in terms of functionality.
Performance is not really relevant in those contexts IMO because it is just an altogether different way of initializing an instance of the model. Unless you are interested in whether or not it is faster to first manually turn your ORM object into a dictionary and pass that to parse_obj
or to just call from_orm
on it. You could benchmark that fairly easily though.
No other functionality of the BaseModel
is affected (performance wise) by that config setting in any way that I can see.
To your second question, I could only speculate. So I will refrain from answering. There is an issue already open for a while that suggests removing the setting altogether, which seems to be kind of in line with your reasoning that it should not be "opt-in" in any case. I am not sure if Samuel Colvin is still accepting backwards-incompatible feature requests for v2, but this issue has not gotten a lot of attention. You might want to participate there.