After looking through the source code, I don't see any way that this would be easily possible with the specialized kwarg supplied. The dict function is recursive and doesn't support arbitrary arguments.
Now, I was able to hack something together but... it is awful. This was performed using pydantic==1.10.7.
I was thinking we could apply a special flag value in the exclude arguments to provide something to trigger the exclusion logic off of. This became trickier than I expected, because it requires knowing the full structure of the object and exactly how to index the excluded fields. There is also some odd normalization happening on lists, which is causing the value provided to be mutated as it makes its way down the function call.
This is the best I could get (WARNING not tested thoroughly). We create a dictionary that returns itself on every lookup, that exposed __all__ as an exclude field. This will allow our key to be passed to each and every model, and be passed on to child objects to evaluate as well.
EXCLUDED_SPECIAL_FIELDS = "exclude_special_fields"
class _ExclusionDict(dict):
def __init__(self):
super().__init__({"__all__": {EXCLUDED_SPECIAL_FIELDS: True}})
def get(self, key):
return self
ExcludeSpecial = _ExclusionDict()
class SpecialExclusionBaseModel(BaseModel):
_special_exclusions: set[str]
def dict(self, **kwargs):
exclusions = getattr(self.__class__, "_special_exclusions", None)
exclude = kwargs.get("exclude")
if exclusions and exclude and EXCLUDED_SPECIAL_FIELDS in exclude:
return {
k: v
for k, v in super().dict(**kwargs).items()
if k not in exclusions
}
return super().dict(**kwargs)
With this base class, we can provide a class field called _special_exclusions to indicate which fields we want excluded when the ExcludeSpecial instance is provided as the exclude kw argument.
On some initial testing, this seems to work with nested hierarchies including dicts and lists. There are probably bugs here that need to be worked out, but hopefully this is a good jumping off point for others.
class MyModel(SpecialExclusionBaseModel):
_special_exclusions = {"field"}
field: str
class AnotherModel(BaseModel):
models: list[MyModel]
class AnotherAnotherModel(BaseModel):
models: dict[str, AnotherModel]
model = MyModel(field=1)
another = AnotherAnotherModel(models={"test": AnotherModel(models=[model])})
print(another.dict(exclude=ExcludeSpecial))
{'models': {'test': {'models': [{}]}}}
Answer from flakes on Stack OverflowAfter looking through the source code, I don't see any way that this would be easily possible with the specialized kwarg supplied. The dict function is recursive and doesn't support arbitrary arguments.
Now, I was able to hack something together but... it is awful. This was performed using pydantic==1.10.7.
I was thinking we could apply a special flag value in the exclude arguments to provide something to trigger the exclusion logic off of. This became trickier than I expected, because it requires knowing the full structure of the object and exactly how to index the excluded fields. There is also some odd normalization happening on lists, which is causing the value provided to be mutated as it makes its way down the function call.
This is the best I could get (WARNING not tested thoroughly). We create a dictionary that returns itself on every lookup, that exposed __all__ as an exclude field. This will allow our key to be passed to each and every model, and be passed on to child objects to evaluate as well.
EXCLUDED_SPECIAL_FIELDS = "exclude_special_fields"
class _ExclusionDict(dict):
def __init__(self):
super().__init__({"__all__": {EXCLUDED_SPECIAL_FIELDS: True}})
def get(self, key):
return self
ExcludeSpecial = _ExclusionDict()
class SpecialExclusionBaseModel(BaseModel):
_special_exclusions: set[str]
def dict(self, **kwargs):
exclusions = getattr(self.__class__, "_special_exclusions", None)
exclude = kwargs.get("exclude")
if exclusions and exclude and EXCLUDED_SPECIAL_FIELDS in exclude:
return {
k: v
for k, v in super().dict(**kwargs).items()
if k not in exclusions
}
return super().dict(**kwargs)
With this base class, we can provide a class field called _special_exclusions to indicate which fields we want excluded when the ExcludeSpecial instance is provided as the exclude kw argument.
On some initial testing, this seems to work with nested hierarchies including dicts and lists. There are probably bugs here that need to be worked out, but hopefully this is a good jumping off point for others.
class MyModel(SpecialExclusionBaseModel):
_special_exclusions = {"field"}
field: str
class AnotherModel(BaseModel):
models: list[MyModel]
class AnotherAnotherModel(BaseModel):
models: dict[str, AnotherModel]
model = MyModel(field=1)
another = AnotherAnotherModel(models={"test": AnotherModel(models=[model])})
print(another.dict(exclude=ExcludeSpecial))
{'models': {'test': {'models': [{}]}}}
You can use model_dump( {nested_modeL_name: {fields_to_exclude} } )
and for nested list of model model_dump(nested_model_name:{"\__all_\_": {fields_to_exclude}} )
ref: https://github.com/pydantic/pydantic/discussions/9382
Recursively exclude all 'None' fields in a deeply nested Pydantic model?
Nested exclude support
python - pydantic exclude multiple fields from model - Stack Overflow
Pydantic v2 exclude parameter in Field and Annotated
So, I am ware of exclude_none, but it works on per-model-basis, meaning that in the case deeply nested models, you would have to add this to every submodel. In certain situations, especially working with 3rd party provided models, this is not really an option. Is there a workaround to this limitation?
Pydantic will exclude the class variables which begin with an underscore. so if it fits your use case, you can rename your attribues.
class User(UserBase):
_user_id=str
some_other_field=str
....
To exclude a field you can also use exclude in Field:
from pydantic import BaseModel, Field
class Mdl(BaseModel):
val: str = Field(
exclude=True,
title="val"
)
however, the advantage of adding excluded parameters in the Config class seems to be that you can get the list of excluded parameters with
print(Mdl.Config.exclude)
I am playing around with Pydantic v2.5 and trying to see how the exclude works when set as a Field option. Let's imagine that I have a User BaseModel class and a Permissions BaseModel class. Both are used in the Config class.
We do not want to print the all User info, hence why I added the exclude in the Permissions class when the user is defined.
from typing import Annotated, Optional
from pydantic import BaseModel, Field
class User(BaseModel):
name: str
surname: str
card_id: str
class Permissions(BaseModel):
user: Annotated[User, Field(exclude=True)]
config_rule: str
class Config:
def __init__(self):
self.user = User(**dict(name="Name", surname="Surname", card_id="12343545"))
self.config_data = Permissions(user=self.user, config_rule="admin")
c = Config()
print(c.config_data.model_dump_json(indent=2))
In this case, the exclude set in the Permissions class seems to be working fine, in fact the print output is:
{
"config_rule": "admin"
}
However, if I change the Permissions class with:
class Permissions(BaseModel):
user: Optional[Annotated[User, Field(exclude=True)]]
config_rule: strThe output is:
{
"user": {
"name": "Name",
"surname": "Surname",
"card_id": "12343545"
},
"config_rule": "admin"
}And I am not sure I understand why. Any thoughts? Thanks.
I have a list of items assigned to users that I want to fetch with a get request. Is there a way to retrieve this in a way that lists assigned_users as a list of usernames and ids instead of the full user model which also includes all the items created by the user and all the items assigned to them?
Pydantic Schema Models: User (id, username, items_created, items_assigned) Item: id, title, created_by, assigned_to
You could exclude only optional model fields that unset by making of union of model fields that are set and those that are not None.
Pydantic provides the following arguments for exporting method model.dict(...):
exclude_unset: whether fields which were not explicitly set when creating the model should be excluded from the returned dictionary; defaultFalse.
exclude_none: whether fields which are equal toNoneshould be excluded from the returned dictionary; defaultFalse
To make union of two dicts we can use the expression a = {**b, **c} (values from c overwrites values from b). Note that since Python 3.9 it could be done just as a = b | c.
from pydantic import BaseModel
from typing import Optional
from pydantic.json import pydantic_encoder
import json
class Foo(BaseModel):
x: int
y: int = 42
z: Optional[int]
def exclude_optional_dict(model: BaseModel):
return {**model.dict(exclude_unset=True), **model.dict(exclude_none=True)}
def exclude_optional_json(model: BaseModel):
return json.dumps(exclude_optional_dict(model), default=pydantic_encoder)
print(exclude_optional_json(Foo(x=3))) # {"x": 3, "y": 42}
print(exclude_optional_json(Foo(x=3, z=None))) # {"x": 3, "z": null, "y": 42}
print(exclude_optional_json(Foo(x=3, z=77))) # {"x": 3, "z": 77, "y": 42}
Update
In order for the approach to work with nested models, we need to do a deep union( or merge) of two dictionaries, like so:
def union(source, destination):
for key, value in source.items():
if isinstance(value, dict):
node = destination.setdefault(key, {})
union(value, node)
else:
destination[key] = value
return destination
def exclude_optional_dict(model: BaseModel):
return union(model.dict(exclude_unset=True), model.dict(exclude_none=True))
class Foo(BaseModel):
x: int
y: int = 42
z: Optional[int]
class Bar(BaseModel):
a: int
b: int = 52
c: Optional[int]
d: Foo
print(exclude_optional_json(Bar(a=4, d=Foo(x=3))))
print(exclude_optional_json(Bar(a=4, c=None, d=Foo(x=3, z=None))))
print(exclude_optional_json(Bar(a=4, c=78, d=Foo(x=3, z=77))))
{"a": 4, "b": 52, "d": {"x": 3, "y": 42}}
{"a": 4, "b": 52, "d": {"x": 3, "y": 42, "z": null}, "c": null}
{"a": 4, "b": 52, "c": 78, "d": {"x": 3, "y": 42, "z": 77}}
If you're using FastAPI then using exclude_none doesn't seem to work when a response_model is mentioned in the route decorator.
@app.post("/items/", response_model=Item)
async def create_item(item: Item):
return item.dict(exclude_none=True)
Fast api seems to reprocess the dict with the pydantic model
So overriding the dict method in the model itself should work
def Item(BaseModel):
name: str
description: Optional[str]
...
def dict(self, *args, **kwargs) -> Dict[str, Any]:
kwargs.pop('exclude_none', None)
return super().dict(*args, exclude_none=True, **kwargs)
(an actual solution would put this definition in separate subclass of BaseModel for reuse)
Note: just changing the default value of the exclude_none keyword argument is not enough: it seems FastAPI always sends exclude_none=False as an argument.
Source:
https://github.com/tiangolo/fastapi/issues/3314#issuecomment-962932368