Changelog
v.2.1.1 (2024.4.20)
- new feature: reading parent node in resolve, post and post_default_handler
v.2.1.0 (2024.04.08)
- bugfix: https://github.com/allmonday/pydantic2-resolve/issues/7
- migrating collctor and internal optimization from pydantic-resolve
v.2.0.0 (2024.02.29)
- rename pydantic2_resolve -> pydantic_resolve to align with pydantic-resolve (v1)
- add more diagrams in readme.
v.1.1.3 (2024.02.08)
- add
model_config
into__all__
v.1.1.2 (2024.02.08) yanked
- add
global_loader_filter
for convinence. (thanks Dambre) - add
model_config
for better schema definition (same as output) and hide fields.
v.1.1.1 (2023.12.20)
- fix corner case of empty list input.
tests/core/test_input.py
v.1.1.0 (2023.12.16)
v1.8.0 + v1.8.1 of pydantic-resolve
- internal refactor: performance improvement
- remove Resolver.annotation_class, it will be processed automatically
- new helper function
copy_dataloader_kls
to generate a copy of DataLoader pydantic_resolve.utils
provide several internalgenerate_loader
functions.
v1.0.1 (2023.11.11)
- optimize TypyAdapter based on pydantic's suggestion
- clean doc
- fix broken examples/
v1.0.0 (2023.11.10)
- adapt to pydantic v2.
pydantic-resolve (v1)
v.1.7.1 (2023.10.19)
- fix raising error when
__pydantic_resolve_exposed__
field value is None
class Bar(BaseModel):
__pydantic_resolve_expose__ = {'num': 'bar_num'} # expose {'bar_num': val }
num: Optional[int] # may be None
v.1.7.0 (2023.9.2)
- add a port in ancestor class to expose value of specific field to its descendants.
- resolve_field and post_field of descendant class can read those from
ancestor_context
- DO NOT EXPOSE resolver_fields (eg Bar.kars, Kar.desc), it is empty by default.
class Kar(BaseModel):
name: str
desc: str = ''
def resolve_desc(self, ancestor_context):
return f"{self.name} - {ancestor_context['bar_num']}" # read ancestor value from 'ancestor_context'
class Bar(BaseModel):
__pydantic_resolve_expose__ = {'num': 'bar_num'} # expose {'bar_num': val }
num: int
kars: List[Kar] = []
def resolve_kars(self):
return [{'name': n} for n in ['a', 'b', 'c']]
class Foo(BaseModel):
nums:List[int]
bars: List[Bar] = []
def resolve_bars(self):
return [{'num': n} for n in self.nums]
v.1.6.5 (2023.8.12)
post_field
method andpost_default_handler
now can read context (setting in Resolver)
v.1.6.4 (2023.8.7)
- some inside refactor
v1.6.3 (2023.7.20)
- fix dataclass exception under lazy annotation. refer to
tests/resolver/test_28_parse_to_obj_for_dataclass_with_annotation.py
- provide extra tip on auto map fails.
recursion dataclass type is still not supported, refer to
test/resolver/test_26_tree.py
, L81
v1.6.2 (2023.7.12)
- fix
output
minor bug
v1.6.1 (2023.7.11)
- fix
output
decorator, it will modify schema_json'srequired
field and fill all fields
v1.6.0 (2023.7.11)
- add
Resolver.context
param - remove
core.resolve
- change
post_method
, return value will be assined to target field. - add
output
decorator - add
post_default_handler
for spacial use.
v1.5.2 (2023.7.10)
- fix pydantic annotation related minor issue
v1.5.1 (2023.7.9)
- fix the order of auto map, which will break the resolve chain
v1.5.0 (2023.7.9) [has bug, do not use]
- new feature. the return value from resolve_method will be automatically converted to the type of target field, which means mapper with params of type is not required any more.
- if you
fromt __future__ import annotations
at top, make sure you define schemas in global scopes.
class Book(BaseModel):
name: str
async def batch_load_fn(keys):
print('oader')
books = [[bb for bb in BOOKS.get(k, [])] for k in keys]
return books
class Student(BaseModel):
id: int
name: str
books: List[Book] = []
def resolve_books(self, loader=LoaderDepend(batch_load_fn)):
return loader.load(self.id) # auto convert dict into Book type
class ClassRoom(BaseModel):
students: List[Student]
students = [Student(id=1, name="jack"), Student(id=2, name="mike"), Student(id=3, name="wiki")]
classroom = ClassRoom(students=students)
classroom = await Resolver().resolve(classroom)
assert isinstance(classroom.students[0].books[0], Book)
- recommend: using
mapper
with lambda only
v1.4.1 (2023.7.6)
- minor optimization, iteration of object attributes.
v1.4.0 (2023.7.6)
- support resolve through objects which intermediate items has not
resolve_
methods.
more detail: tests/resolver/test_21_not_stop_by_idle_level.py
class C(BaseModel):
name: str = ''
class B(BaseModel):
name: str
c: Optional[C] = None
async def resolve_c(self) -> Optional[C]:
await asyncio.sleep(1)
return C(name='hello world')
class A(BaseModel):
b: B
class Z(BaseModel):
a: A
resolve_age: int
@pytest.mark.asyncio
async def test_resolve_object():
s = Z(a=A(b=B(name="kikodo")), resolve_age=21) # resolve starts from B
result = await Resolver().resolve(s)
v1.3.2 (2023.7.4)
- add subset check decorator
ensure_subset
.
class Base(BaseModel):
a: str
b: int
@util.ensure_subset(Base)
class ChildA(BaseModel):
a: str
@util.ensure_subset(Base)
class ChildB(BaseModel):
a: str
c: int = 0
def resolve_c(self):
return 21
@util.ensure_subset(Base)
class ChildB(BaseModel):
a: int # raise attribute error
v1.3.1 (2023.7.3)
- support
auto-mapping
from pydantic to pydantic and fix some testcases.
more detail: tests/resolver/test_16_mapper.py
- test_mapper_6
- test_mapper_7
v1.3.0 (2023.6.27)
- add
Resolver.loader_instances
param, user can create loader before Resolver and this loader will be used inside. for example: you can prime value and to avoid extra query.
loader = FriendLoader()
loader.prime('tangkikodo', ['tom', 'jerry'])
loader.prime('john', ['mike', 'wallace'])
result = await Resolver(loader_instances={FriendLoader: loader}).resolve(root)
# batch_load_fn will not run.
more detail: tests/resolver/test_20_loader_instance.py
v1.2.2 (2023.6.21)
- minor adjustment,
build_list
andbuild_object
will return iterator instead of list.
v1.2.1 (2023.6.19)
- fix, modify
post_fieldname
execution position, reduce the duplication.
v1.2.0 (2023.6.18)
- add
post_fieldname
method, it will be called after the object is fully resolve as a hook, developer can run some aggregation computation.tests/resolver/test_18_post_methods.py
class Friend(BaseModel):
name: str
cash: Optional[Cash] = None
@mapper(Cash) # auto mapping
def resolve_cash(self, contact_loader=LoaderDepend(cash_batch_load_fn)):
return contact_loader.load(self.name)
has_cash: bool = False
def post_has_cash(self): # <----------------------
self.has_cash = self.cash is not None
class User(BaseModel):
name: str
age: int
friends: List[Friend] = []
@mapper(lambda names: [Friend(name=name) for name in names])
def resolve_friends(self, friend_loader=LoaderDepend(friends_batch_load_fn)):
return friend_loader.load(self.name)
has_cash: bool = False
def post_has_cash(self): # <----------------------
self.has_cash = any([f.has_cash for f in self.friends])
v1.1.1 (2023.6.17)
- extend @mapper decorator with target class option, this will call auto_mapping function inside.
comments: List[CommentSchema] = []
@mapper(CommentSchema)
def resolve_comments(self, loader=LoaderDepend(CommentLoader)):
return loader.load(self.id)
v1.1.0 (2023.6.16)
- add @mapper decorator, to enable custom data transform
comments: List[CommentSchema] = []
@mapper(lambda items: [CommentSchema.from_orm(item) for item in items])
def resolve_comments(self, loader=LoaderDepend(CommentLoader)):
return loader.load(self.id)
v1.0.0 (2023.6.11)
- support
batch_load_fn
as params forLoaderDepend
- add test
tests/resolver/test_15_support_batch_load_fn.py
build_object
,build_list
can be imported frompydantic_resolve
v0.5.1 (2023.6.11)
- add helper utils for Dataloader.batch_load_fn
built_list
andbuild_object
, seeexamples/fastapi_demo/loader.py
- FIX: fix potential error caused by same loader name from different module. see
tests/resolver/test_14_check_loader_name.py
v0.5.0 (2023.6.1)
Resolver.ensure_type
: True will ensureresolve_*
methods have return annotation.- add FastAPI integrated example
examples/fastapi_demo
v0.4.0 (2023.4.6)
- add new install option
pip install "pydantic-resolve[dataloader]" to include
aiodataloader` by default - add new
doc/loader-cn.md
,doc/loader-en.md
to explain the convinence of usingLoaderDepen
- add new params in Resolver:
loader_filters
to support global filter setting for inside loaders. - add
examples/6_sqlalchemy_loaderdepend_global_filter.md
for global filter
v0.3.2 (2023.4.5)
- refact tests, group by
core
andresolver
- replace
unittest
withpytest.mark.asyncio
- little
readme.md
change, new top sample code
v0.3.1 (2023.4.3)
- change code examples in readme.md
- add unittest for pydantic validationError
- code refactor (rename)
v0.3.0
- add
DataloaderDependCantBeResolved
, it will raise if useresolve
to handle schema withDataloader
- add
chinese.md
andenglish.md
indoc
folder