mirror of
https://github.com/aiogram/aiogram.git
synced 2026-04-08 16:37:47 +00:00
refactor(backend): implement default value handling in serialization
The `json_serialize` method has been added to the `TelegramMethod` and `TelegramObject` classes to replace `Default` placeholders with actual values from the bot's defaults during JSON serialization. This change ensures that non-standard objects are handled correctly, maintaining backward compatibility for built-in pydantic json serialization. This modification is beneficial as it centralizes the handling of default values when serializing objects to JSON, making the code more maintainable and robust against future changes in serialization logic.
This commit is contained in:
parent
7653895dd2
commit
6f4452f4e0
3 changed files with 71 additions and 7 deletions
|
|
@ -12,11 +12,16 @@ from typing import (
|
|||
TypeVar,
|
||||
)
|
||||
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
from pydantic.functional_validators import model_validator
|
||||
from pydantic import (
|
||||
BaseModel,
|
||||
ConfigDict,
|
||||
SerializerFunctionWrapHandler,
|
||||
model_serializer,
|
||||
model_validator,
|
||||
)
|
||||
|
||||
from aiogram.client.context_controller import BotContextController
|
||||
|
||||
from aiogram.client.default import Default, DefaultBotProperties
|
||||
from ..types import InputFile, ResponseParameters
|
||||
from ..types.base import UNSET_TYPE
|
||||
|
||||
|
|
@ -65,6 +70,22 @@ class TelegramMethod(BotContextController, BaseModel, Generic[TelegramType], ABC
|
|||
return values
|
||||
return {k: v for k, v in values.items() if not isinstance(v, UNSET_TYPE)}
|
||||
|
||||
@model_serializer(mode="wrap", when_used="json")
|
||||
def json_serialize(self, handler: SerializerFunctionWrapHandler) -> Dict[str, Any]:
|
||||
"""
|
||||
Replacing `Default` placeholders with actual values from bot defaults.
|
||||
Ensures JSON serialization backward compatibility by handling non-standard objects.
|
||||
"""
|
||||
if not isinstance(self, TelegramMethod):
|
||||
return handler(self)
|
||||
properties = self.bot.default if self.bot else DefaultBotProperties()
|
||||
default_fields = {
|
||||
field: properties[value.name]
|
||||
for field in self.model_fields.keys()
|
||||
if isinstance(value := getattr(self, field), Default)
|
||||
}
|
||||
return handler(self.model_copy(update=default_fields))
|
||||
|
||||
if TYPE_CHECKING:
|
||||
__returning__: ClassVar[type]
|
||||
__api_method__: ClassVar[str]
|
||||
|
|
|
|||
|
|
@ -1,10 +1,16 @@
|
|||
from typing import Any, Dict
|
||||
from unittest.mock import sentinel
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, model_validator
|
||||
from pydantic import (
|
||||
BaseModel,
|
||||
ConfigDict,
|
||||
SerializerFunctionWrapHandler,
|
||||
model_serializer,
|
||||
model_validator,
|
||||
)
|
||||
|
||||
from aiogram.client.context_controller import BotContextController
|
||||
from aiogram.client.default import Default
|
||||
from aiogram.client.default import Default, DefaultBotProperties
|
||||
|
||||
|
||||
class TelegramObject(BotContextController, BaseModel):
|
||||
|
|
@ -36,6 +42,22 @@ class TelegramObject(BotContextController, BaseModel):
|
|||
return values
|
||||
return {k: v for k, v in values.items() if not isinstance(v, UNSET_TYPE)}
|
||||
|
||||
@model_serializer(mode="wrap", when_used="json")
|
||||
def json_serialize(self, handler: SerializerFunctionWrapHandler) -> Dict[str, Any]:
|
||||
"""
|
||||
Replacing `Default` placeholders with actual values from bot defaults.
|
||||
Ensures JSON serialization backward compatibility by handling non-standard objects.
|
||||
"""
|
||||
if not isinstance(self, TelegramObject):
|
||||
return handler(self)
|
||||
properties = self.bot.default if self.bot else DefaultBotProperties()
|
||||
default_fields = {
|
||||
field: properties[value.name]
|
||||
for field in self.model_fields.keys()
|
||||
if isinstance(value := getattr(self, field), Default)
|
||||
}
|
||||
return handler(self.model_copy(update=default_fields))
|
||||
|
||||
|
||||
class MutableTelegramObject(TelegramObject):
|
||||
model_config = ConfigDict(
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
from typing import Any, Dict
|
||||
from unittest.mock import sentinel
|
||||
|
||||
import pytest
|
||||
|
||||
from aiogram.methods import GetMe, TelegramMethod
|
||||
from aiogram.types import TelegramObject, User
|
||||
from aiogram.client.default import Default
|
||||
from aiogram.methods import GetMe, SendMessage, TelegramMethod
|
||||
from aiogram.types import LinkPreviewOptions, TelegramObject, User
|
||||
from tests.mocked_bot import MockedBot
|
||||
|
||||
|
||||
|
|
@ -26,6 +28,25 @@ class TestTelegramMethodRemoveUnset:
|
|||
assert obj.remove_unset("") == ""
|
||||
|
||||
|
||||
class TestTelegramMethodJsonSerialize:
|
||||
@pytest.mark.parametrize(
|
||||
"obj",
|
||||
[
|
||||
SendMessage(
|
||||
chat_id=1,
|
||||
text="test",
|
||||
),
|
||||
LinkPreviewOptions(),
|
||||
],
|
||||
)
|
||||
def test_json_serialize(self, obj):
|
||||
def has_defaults(dump: Dict[str, Any]) -> bool:
|
||||
return any(isinstance(value, Default) for value in dump.values())
|
||||
|
||||
assert has_defaults(obj.model_dump())
|
||||
assert not has_defaults(obj.model_dump(mode="json"))
|
||||
|
||||
|
||||
class TestTelegramMethodCall:
|
||||
async def test_async_emit_unsuccessful(self, bot: MockedBot):
|
||||
with pytest.raises(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue