New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add advanced exclude and include support for dict, json and copy #648
Conversation
@JrooTJunior, @senpos, check this out, please |
Codecov Report
@@ Coverage Diff @@
## master #648 +/- ##
=====================================
Coverage 100% 100%
=====================================
Files 15 15
Lines 2673 2713 +40
Branches 530 537 +7
=====================================
+ Hits 2673 2713 +40 |
Add new version section (v0.31)
@samuelcolvin, what do you think about changes? I guess I should write tests for ValueExclude class, so I'll do it a bit later, if you don't mind. |
Rename ValueExclude to ValueItems and move it to utils Use old logic to calculate keys, but still exclude it in _iter
# Conflicts: # HISTORY.rst
This will increase speed when no include or exclude given and skip_defaults is False
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
please discuss |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks great! but it's a big change and I need to play with it more and convince myself:
- it doesn't slow things down
- it won't break people's current (mis)use of pydantic
- it doesn't stop us fixing [Question] Looping through a nested model object returns dictionaries? #508 when we're ready for a breaking change
- the whole
_iter
vs._get_values
thing looks a bit ugly, but maybe it has to be
@MrMrRobat please could you rebase and update HISTORY, otherwise I'll go through it more later in the week.
@@ -487,17 +511,56 @@ def _decompose_class(cls: Type['Model'], obj: Any) -> GetterDict: | |||
return GetterDict(obj) | |||
|
|||
@classmethod | |||
def _get_value(cls, v: Any, by_alias: bool, skip_defaults: bool) -> Any: | |||
@no_type_check |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why do we need this? maybe better to have a typing ignore
comment on a few lines?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It will be on every line we call this method, so I thought having decorator would be better. But yeah, I can replace it with # type: ignore
if you wish
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no that's. Sounds like I need to look at this more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
MyPy throws error here because we use and
operator on exclude=value_items and value_items.for_value(k)
pydantic/main.py:597: error: Argument "exclude" to "_get_value" of "BaseModel" has incompatible type "Union[ValueItems, None, Any]"; expected "Union[Set[Union[int, str]], Dict[Union[int, str], Any], None]"
So it thinks that potentially we can pass an instance of ValueItems
itseld, but since bool(ValueItems())
always will be equivalent for True
, it will never happen, so I guess we absolutely ok here.
I guess it definitely will slow something down. Unfortunately I didn't run any comparison benchmarks. So please let me know what you find out
Agree on that and will appreciate any suggestions how we can make this look better
@samuelcolvin yep, done. |
Okay, I've made a few tweaks, but in general I think this is good. I've tested the performance with import json
import timeit
from datetime import datetime
from pathlib import Path
from typing import List
from pydantic import BaseModel, PositiveInt, ValidationError, constr
THIS_DIR = Path(__file__).parent
def main():
class Model(BaseModel):
id: int
client_name: constr(max_length=255)
sort_index: float
# client_email: EmailStr = None
client_phone: constr(max_length=255) = None
class Location(BaseModel):
latitude: float = None
longitude: float = None
location: Location = None
contractor: PositiveInt = None
upstream_http_referrer: constr(max_length=1023) = None
grecaptcha_response: constr(min_length=20, max_length=1000)
last_updated: datetime = None
class Skill(BaseModel):
subject: str
subject_id: int
category: str
qual_level: str
qual_level_id: int
qual_level_ranking: float = 0
skills: List[Skill] = []
json_path = THIS_DIR / 'benchmarks' / 'cases.json'
with json_path.open() as f:
cases = json.load(f)
results = []
for case in cases:
try:
m = Model(**case)
except ValidationError:
pass
else:
# return m
r = m.dict(exclude={'location', 'grecaptcha_response'})
results.append(r)
if __name__ == '__main__':
repeats = 20
v = timeit.timeit(main, number=repeats)
print(f'time taken: {v / repeats * 1000:0.3f}ms') ( Basically this work doesn't seem to make a significant difference. @MrMrRobat please confirm you're happy with this, if so I'll merge. |
Great, I’m absolutely happy with this. Thanks for your tweaks :) |
@MrMrRobat sorry, I forgot. I guess we need some docs to explain how this works. If you could do that I'll merge. |
@samuelcolvin, docs is written, check it out please |
Change Summary
Implementation of advanced exclude support. With this changes you will be able to exclude any field on any depth of a model.
_iter
(when dealing with BaseModel) and_get_values
(when dealing with raw dicts and sequences) methods of model, not indict
orcopy
methods themselfs._iter
and_get_value
as well(just modified_calculate_keys
replaced with_update_exclude
which is calculating keys to exclude_calculate_keys
a bit to agree with changes)ValueItems
(used in_get_value
and_iter
) for more convenient calculation of excluded and included fields on valuesExample:
Related issue number
#640
Checklist
HISTORY.rst
has been updated#<number>
@<whomever>