Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 22 additions & 14 deletions ormar/models/helpers/relations.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,20 +192,28 @@ def serialize(
by excluding the children.
"""
try:
with warnings.catch_warnings():
warnings.filterwarnings(
"ignore", message="Pydantic serializer warnings"
)
return handler(children)
except ValueError as exc: # pragma: no cover
if not str(exc).startswith("Circular reference"):
raise exc

result = []
for child in children:
# If there is one circular ref dump all children as pk only
result.append({child.ormar_config.pkname: child.pk})
return result
try:
with warnings.catch_warnings():
warnings.filterwarnings(
"ignore", message="Pydantic serializer warnings"
)
return handler(children)
except ValueError as exc: # pragma: no cover
if not str(exc).startswith("Circular reference"):
raise exc

result = []
for child in children:
if not hasattr(child, "ormar_config"):
continue
# If there is one circular ref dump all children as pk only
result.append({child.ormar_config.pkname: child.pk})
return result
except ReferenceError:
# Pydantic >= 2.13 may invoke this serializer with weakref
# children whose referent is already gone (e.g. when ormar's
# late metaclass rebuild changed dispatch ordering).
return None

decorator = field_serializer(related_name, mode="wrap", check_fields=False)(
serialize
Expand Down
45 changes: 34 additions & 11 deletions ormar/models/metaclass.py
Original file line number Diff line number Diff line change
Expand Up @@ -632,17 +632,26 @@ def serialize(
Serialize a value if it's not expired weak reference.
"""
try:
with warnings.catch_warnings():
warnings.filterwarnings(
"ignore", message="Pydantic serializer warnings"
)
return handler(value)
try:
with warnings.catch_warnings():
warnings.filterwarnings(
"ignore", message="Pydantic serializer warnings"
)
return handler(value)
except ValueError as exc:
if not str(exc).startswith("Circular reference"):
raise exc
# Pydantic >= 2.13 dispatches the wrap serializer with
# broader value types (e.g. QuerysetProxy for M2M reverse
# accessors) where 2.12 unwrapped to Model first. Earlier
# pydantic versions never reach this branch with a non-
# Model value, so the None return is uncovered on the
# 2.11 floor pinned in CI.
if value is None or not hasattr(value, "ormar_config"):
return None # pragma: no cover
return {value.ormar_config.pkname: value.pk}
except ReferenceError:
return None
except ValueError as exc:
if not str(exc).startswith("Circular reference"):
raise exc
return {value.ormar_config.pkname: value.pk} if value else None

return serialize

Expand Down Expand Up @@ -753,10 +762,24 @@ def __new__( # type: ignore # noqa: CCR001
None,
)
)
new_model.model_rebuild(force=True)

new_model.pk = PkDescriptor(name=new_model.ormar_config.pkname)

if not (
new_model.ormar_config.abstract
or new_model.ormar_config.proxy
or new_model.ormar_config.requires_ref_update
):
# Pydantic >= 2.13 changed how TypeAdapter(Annotated[T,
# FieldInfo(...)]) resolves nested schemas — it now uses
# the cached child snapshot instead of walking the live
# model. Reverse-relation fields added by
# expand_reverse_relationships above become invisible
# from nested dumps unless we invalidate the cached
# schema. Mark incomplete so the next access (or the
# FastAPI-style TypeAdapter wrapper) recompiles afresh
# without the stale snapshot.
new_model.__pydantic_complete__ = False # type: ignore[attr-defined]

return new_model

@property
Expand Down
22 changes: 11 additions & 11 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,9 @@ classifiers = [

[tool.poetry.dependencies]
python = "^3.10.0"
pydantic = "^2.11.9"
# Pinned below 2.14 because ormar relies on pydantic internals; bump the
# upper bound manually after verifying compatibility with each new release.
pydantic = ">=2.11.9,<2.14"
SQLAlchemy = {version = "^2.0.40", extras = ["asyncio"]}
cryptography = { version = ">=44.0.1,<49.0.0", optional = true }
# Async database drivers
Expand Down