From 23e7bd1514323b9b87f9b08204a8933bc058ece1 Mon Sep 17 00:00:00 2001 From: Seth Buffenbarger Date: Thu, 3 Apr 2025 16:55:58 -0400 Subject: [PATCH 1/3] handle default issue with multiple inheritance --- aredis_om/model/model.py | 12 +++++++----- tests/test_hash_model.py | 37 +++++++++++++++++++++++++++++++++++++ tests/test_json_model.py | 37 +++++++++++++++++++++++++++++++++++++ 3 files changed, 81 insertions(+), 5 deletions(-) diff --git a/aredis_om/model/model.py b/aredis_om/model/model.py index 8018ec8a..abf6964a 100644 --- a/aredis_om/model/model.py +++ b/aredis_om/model/model.py @@ -1320,11 +1320,13 @@ def __new__(cls, name, bases, attrs, **kwargs): # noqa C901 meta = meta or getattr(new_class, "Meta", None) base_meta = getattr(new_class, "_meta", None) - if len(bases) == 1: - for f_name in bases[0].model_fields: - field = bases[0].model_fields[f_name] - print(field) - new_class.model_fields[f_name] = field + if len(bases) >= 1: + for base_index in range(len(bases)): + model_fields = getattr(bases[base_index], "model_fields", []) + for f_name in model_fields: + field = model_fields[f_name] + print(field) + new_class.model_fields[f_name] = field if meta and meta != DefaultMeta and meta != base_meta: new_class.Meta = meta diff --git a/tests/test_hash_model.py b/tests/test_hash_model.py index 875bfdb7..439f8c37 100644 --- a/tests/test_hash_model.py +++ b/tests/test_hash_model.py @@ -976,3 +976,40 @@ class Child(Model): assert rematerialized.age == 18 assert rematerialized.bio is None + +@py_test_mark_asyncio +async def test_grandchild_class_expression_proxy(): + # https://github.com/redis/redis-om-python/issues/669 seeing weird issue with child classes initalizing all their undefined members as ExpressionProxies + class Model(HashModel): + first_name: str + last_name: str + age: int = Field(default=18) + bio: Optional[str] = Field(default=None) + + class Child(Model): + other_name: str = "test" + + class GrandChild(Child): + grand_name: str = "test" + + class GreatGrandChild(GrandChild): + great_name: str = "test" + + await Migrator().run() + m = GreatGrandChild(first_name="Steve", last_name="Lorello") + assert m.age == 18 + await m.save() + + assert m.age == 18 + assert m.other_name == "test" + assert m.grand_name == "test" + assert m.great_name == "test" + + rematerialized = await GreatGrandChild.find(GreatGrandChild.pk == m.pk).first() + + assert rematerialized.age == 18 + assert rematerialized.age != 19 + assert rematerialized.bio is None + assert rematerialized.other_name == "test" + assert rematerialized.grand_name == "test" + assert rematerialized.great_name == "test" diff --git a/tests/test_json_model.py b/tests/test_json_model.py index 5677d9e8..75771c60 100644 --- a/tests/test_json_model.py +++ b/tests/test_json_model.py @@ -1195,6 +1195,43 @@ class Child(Model): assert rematerialized.age != 19 assert rematerialized.bio is None +@py_test_mark_asyncio +async def test_grandchild_class_expression_proxy(): + # https://github.com/redis/redis-om-python/issues/669 seeing weird issue with child classes initalizing all their undefined members as ExpressionProxies + class Model(JsonModel): + first_name: str + last_name: str + age: int = Field(default=18) + bio: Optional[str] = Field(default=None) + + class Child(Model): + is_new: bool = True + + class GrandChild(Child): + is_newer: bool = True + + class GreatGrandChild(GrandChild): + is_great: bool = True + + await Migrator().run() + m = GreatGrandChild(first_name="Steve", last_name="Lorello") + assert m.age == 18 + await m.save() + + assert m.age == 18 + assert m.is_new is True + assert m.is_newer is True + assert m.is_great is True + + rematerialized = await GreatGrandChild.find(GreatGrandChild.pk == m.pk).first() + + assert rematerialized.age == 18 + assert rematerialized.age != 19 + assert rematerialized.bio is None + assert rematerialized.is_new is True + assert rematerialized.is_newer is True + assert rematerialized.is_great is True + @py_test_mark_asyncio async def test_merged_model_error(): class Player(EmbeddedJsonModel): From df495c59d6aeafa314421976e0df9ab3b72401bf Mon Sep 17 00:00:00 2001 From: Seth Buffenbarger Date: Fri, 4 Apr 2025 08:15:43 -0400 Subject: [PATCH 2/3] update test to reproduce error --- tests/test_hash_model.py | 31 ++++++++++--------------------- tests/test_json_model.py | 34 +++++++++++----------------------- 2 files changed, 21 insertions(+), 44 deletions(-) diff --git a/tests/test_hash_model.py b/tests/test_hash_model.py index 439f8c37..12df8cda 100644 --- a/tests/test_hash_model.py +++ b/tests/test_hash_model.py @@ -18,6 +18,7 @@ Migrator, NotFoundError, QueryNotSupportedError, + RedisModel, RedisModelError, ) @@ -977,39 +978,27 @@ class Child(Model): assert rematerialized.age == 18 assert rematerialized.bio is None + @py_test_mark_asyncio -async def test_grandchild_class_expression_proxy(): +async def test_child_class_expression_proxy_with_mixin(): # https://github.com/redis/redis-om-python/issues/669 seeing weird issue with child classes initalizing all their undefined members as ExpressionProxies - class Model(HashModel): + class Model(RedisModel, abc.ABC): first_name: str last_name: str age: int = Field(default=18) bio: Optional[str] = Field(default=None) - class Child(Model): - other_name: str = "test" - - class GrandChild(Child): - grand_name: str = "test" - - class GreatGrandChild(GrandChild): - great_name: str = "test" + class Child(Model, HashModel): + other_name: str + # is_new: bool = Field(default=True) await Migrator().run() - m = GreatGrandChild(first_name="Steve", last_name="Lorello") - assert m.age == 18 + m = Child(first_name="Steve", last_name="Lorello", other_name="foo") await m.save() - + print(m.age) assert m.age == 18 - assert m.other_name == "test" - assert m.grand_name == "test" - assert m.great_name == "test" - rematerialized = await GreatGrandChild.find(GreatGrandChild.pk == m.pk).first() + rematerialized = await Child.find(Child.pk == m.pk).first() assert rematerialized.age == 18 - assert rematerialized.age != 19 assert rematerialized.bio is None - assert rematerialized.other_name == "test" - assert rematerialized.grand_name == "test" - assert rematerialized.great_name == "test" diff --git a/tests/test_json_model.py b/tests/test_json_model.py index 75771c60..a3e4d40f 100644 --- a/tests/test_json_model.py +++ b/tests/test_json_model.py @@ -21,6 +21,7 @@ Migrator, NotFoundError, QueryNotSupportedError, + RedisModel, RedisModelError, ) @@ -1160,7 +1161,6 @@ class TestLiterals(JsonModel): assert rematerialized.pk == item.pk - @py_test_mark_asyncio async def test_two_false_pks(): from pydantic_core import PydanticUndefined as Undefined @@ -1171,6 +1171,7 @@ class SomeModel(JsonModel): SomeModel(field1="foo", field2="bar") + @py_test_mark_asyncio async def test_child_class_expression_proxy(): # https://github.com/redis/redis-om-python/issues/669 seeing weird issue with child classes initalizing all their undefined members as ExpressionProxies @@ -1195,42 +1196,30 @@ class Child(Model): assert rematerialized.age != 19 assert rematerialized.bio is None + @py_test_mark_asyncio -async def test_grandchild_class_expression_proxy(): - # https://github.com/redis/redis-om-python/issues/669 seeing weird issue with child classes initalizing all their undefined members as ExpressionProxies - class Model(JsonModel): +async def test_child_class_expression_proxy_with_mixin(): + class Model(RedisModel, abc.ABC): first_name: str last_name: str age: int = Field(default=18) bio: Optional[str] = Field(default=None) - class Child(Model): - is_new: bool = True - - class GrandChild(Child): - is_newer: bool = True - - class GreatGrandChild(GrandChild): - is_great: bool = True + class Child(Model, JsonModel): + is_new: bool = Field(default=True) await Migrator().run() - m = GreatGrandChild(first_name="Steve", last_name="Lorello") - assert m.age == 18 + m = Child(first_name="Steve", last_name="Lorello") await m.save() - + print(m.age) assert m.age == 18 - assert m.is_new is True - assert m.is_newer is True - assert m.is_great is True - rematerialized = await GreatGrandChild.find(GreatGrandChild.pk == m.pk).first() + rematerialized = await Child.find(Child.pk == m.pk).first() assert rematerialized.age == 18 assert rematerialized.age != 19 assert rematerialized.bio is None - assert rematerialized.is_new is True - assert rematerialized.is_newer is True - assert rematerialized.is_great is True + @py_test_mark_asyncio async def test_merged_model_error(): @@ -1246,4 +1235,3 @@ class Game(JsonModel): ) print(q.query) assert q.query == "(@player1_username:{username})| (@player2_username:{username})" - From 8f4aa47ef8748451aa75f66bfe57922b414a85da Mon Sep 17 00:00:00 2001 From: Seth Buffenbarger Date: Fri, 4 Apr 2025 08:42:11 -0400 Subject: [PATCH 3/3] remove print --- aredis_om/model/model.py | 1 - 1 file changed, 1 deletion(-) diff --git a/aredis_om/model/model.py b/aredis_om/model/model.py index abf6964a..67769cd3 100644 --- a/aredis_om/model/model.py +++ b/aredis_om/model/model.py @@ -1325,7 +1325,6 @@ def __new__(cls, name, bases, attrs, **kwargs): # noqa C901 model_fields = getattr(bases[base_index], "model_fields", []) for f_name in model_fields: field = model_fields[f_name] - print(field) new_class.model_fields[f_name] = field if meta and meta != DefaultMeta and meta != base_meta: