Fix add_field_serializer_for_reverse_relations clearing validators (#1302)
* Fix add_field_serializer_for_reverse_relations clearing validators * add test to check that validators are not removed * compatibility with old python * fix test default values * fix coverage and cleanup --------- Co-authored-by: collerek <collerek@gmail.com>
This commit is contained in:
@ -5,9 +5,7 @@ from typing import TYPE_CHECKING, Any, List, Optional, Type, Union, cast
|
|||||||
from pydantic import BaseModel, create_model, field_serializer
|
from pydantic import BaseModel, create_model, field_serializer
|
||||||
from pydantic._internal._decorators import DecoratorInfos
|
from pydantic._internal._decorators import DecoratorInfos
|
||||||
from pydantic.fields import FieldInfo
|
from pydantic.fields import FieldInfo
|
||||||
from pydantic_core.core_schema import (
|
from pydantic_core.core_schema import SerializerFunctionWrapHandler
|
||||||
SerializerFunctionWrapHandler,
|
|
||||||
)
|
|
||||||
|
|
||||||
import ormar
|
import ormar
|
||||||
from ormar import ForeignKey, ManyToMany
|
from ormar import ForeignKey, ManyToMany
|
||||||
@ -174,8 +172,8 @@ def add_field_serializer_for_reverse_relations(
|
|||||||
"ignore", message="Pydantic serializer warnings"
|
"ignore", message="Pydantic serializer warnings"
|
||||||
)
|
)
|
||||||
return handler(children)
|
return handler(children)
|
||||||
except ValueError as exc:
|
except ValueError as exc: # pragma: no cover
|
||||||
if not str(exc).startswith("Circular reference"): # pragma: no cover
|
if not str(exc).startswith("Circular reference"):
|
||||||
raise exc
|
raise exc
|
||||||
|
|
||||||
result = []
|
result = []
|
||||||
@ -188,7 +186,18 @@ def add_field_serializer_for_reverse_relations(
|
|||||||
serialize
|
serialize
|
||||||
)
|
)
|
||||||
setattr(to_model, f"serialize_{related_name}", decorator)
|
setattr(to_model, f"serialize_{related_name}", decorator)
|
||||||
DecoratorInfos.build(to_model)
|
# DecoratorInfos.build will overwrite __pydantic_decorators__ on to_model,
|
||||||
|
# deleting the previous decorators. We need to save them and then merge them.
|
||||||
|
prev_decorators = getattr(to_model, "__pydantic_decorators__", DecoratorInfos())
|
||||||
|
new_decorators = DecoratorInfos.build(to_model)
|
||||||
|
prev_decorators.validators.update(new_decorators.validators)
|
||||||
|
prev_decorators.field_validators.update(new_decorators.field_validators)
|
||||||
|
prev_decorators.root_validators.update(new_decorators.root_validators)
|
||||||
|
prev_decorators.field_serializers.update(new_decorators.field_serializers)
|
||||||
|
prev_decorators.model_serializers.update(new_decorators.model_serializers)
|
||||||
|
prev_decorators.model_validators.update(new_decorators.model_validators)
|
||||||
|
prev_decorators.computed_fields.update(new_decorators.computed_fields)
|
||||||
|
setattr(to_model, "__pydantic_decorators__", prev_decorators)
|
||||||
|
|
||||||
|
|
||||||
def replace_models_with_copy(
|
def replace_models_with_copy(
|
||||||
|
|||||||
@ -97,7 +97,25 @@ async def test_related_with_defaults(sample_data):
|
|||||||
"year": 2021,
|
"year": 2021,
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"country": {"authors": [{"id": 1}], "id": 1},
|
"country": {
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"books": [
|
||||||
|
{
|
||||||
|
"author": {"id": 1},
|
||||||
|
"id": 1,
|
||||||
|
"title": "Bug caused by " "default value",
|
||||||
|
"year": 2021,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"country": {"id": 1},
|
||||||
|
"id": 1,
|
||||||
|
"name": "bug",
|
||||||
|
"rating": 5,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"id": 1,
|
||||||
|
},
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"name": "bug",
|
"name": "bug",
|
||||||
"rating": 5,
|
"rating": 5,
|
||||||
|
|||||||
@ -0,0 +1,48 @@
|
|||||||
|
from typing import List, Optional, Union
|
||||||
|
|
||||||
|
import ormar
|
||||||
|
import pytest_asyncio
|
||||||
|
from pydantic import field_validator
|
||||||
|
|
||||||
|
from tests.lifespan import init_tests
|
||||||
|
from tests.settings import create_config
|
||||||
|
|
||||||
|
base_ormar_config = create_config()
|
||||||
|
|
||||||
|
|
||||||
|
class Author(ormar.Model):
|
||||||
|
ormar_config = base_ormar_config.copy()
|
||||||
|
|
||||||
|
id: int = ormar.Integer(primary_key=True)
|
||||||
|
name: str = ormar.String(max_length=80)
|
||||||
|
|
||||||
|
@field_validator("name", mode="before")
|
||||||
|
@classmethod
|
||||||
|
def validate_name(cls, v: Union[str, List[str]]) -> str:
|
||||||
|
if isinstance(v, list):
|
||||||
|
v = " ".join(v)
|
||||||
|
return v
|
||||||
|
|
||||||
|
|
||||||
|
class Post(ormar.Model):
|
||||||
|
ormar_config = base_ormar_config.copy()
|
||||||
|
|
||||||
|
id: int = ormar.Integer(primary_key=True)
|
||||||
|
title: str = ormar.String(max_length=200)
|
||||||
|
author: Optional[Author] = ormar.ForeignKey(Author)
|
||||||
|
|
||||||
|
|
||||||
|
create_test_database = init_tests(base_ormar_config)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest_asyncio.fixture(scope="function", autouse=True)
|
||||||
|
async def cleanup():
|
||||||
|
yield
|
||||||
|
async with base_ormar_config.database:
|
||||||
|
await Post.objects.delete(each=True)
|
||||||
|
await Author.objects.delete(each=True)
|
||||||
|
|
||||||
|
|
||||||
|
def test_validator():
|
||||||
|
author = Author(name=["Test", "Author"])
|
||||||
|
assert author.name == "Test Author"
|
||||||
Reference in New Issue
Block a user