Skip to content

Commit b2504c5

Browse files
authored
Merge branch 'main' into fix/647
2 parents 5007515 + 8e39ee0 commit b2504c5

File tree

8 files changed

+139
-9
lines changed

8 files changed

+139
-9
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ jobs:
7676
strategy:
7777
matrix:
7878
os: [ ubuntu-latest ]
79-
pyver: [ "3.8", "3.9", "3.10", "3.11", "3.12", "pypy-3.8", "pypy-3.9", "pypy-3.10" ]
79+
pyver: [ "3.9", "3.10", "3.11", "3.12", "pypy-3.9", "pypy-3.10" ]
8080
redisstack: [ "latest" ]
8181
fail-fast: false
8282
services:

.github/workflows/pypi-publish.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ jobs:
4242
4343
- name: Set Poetry config
4444
run: |
45-
pip install --upgrade pip poetry
45+
pip install --upgrade pip poetry==1.8.4
4646
poetry config virtualenvs.in-project false
4747
poetry config virtualenvs.path ~/.virtualenvs
4848
poetry export --dev --without-hashes -o requirements.txt

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<div align="center">
22
<br/>
33
<br/>
4-
<img width="360" src="https://raw.githubusercontent.com/redis-developer/redis-om-python/main/images/logo.svg?token=AAAXXHUYL6RHPESRRAMBJOLBSVQXE" alt="Redis OM" />
4+
<img width="360" src="docs/images/logo.svg" alt="Redis OM" />
55
<br/>
66
<br/>
77
</div>

aredis_om/model/model.py

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1320,6 +1320,13 @@ def __new__(cls, name, bases, attrs, **kwargs): # noqa C901
13201320
meta = meta or getattr(new_class, "Meta", None)
13211321
base_meta = getattr(new_class, "_meta", None)
13221322

1323+
if len(bases) >= 1:
1324+
for base_index in range(len(bases)):
1325+
model_fields = getattr(bases[base_index], "model_fields", [])
1326+
for f_name in model_fields:
1327+
field = model_fields[f_name]
1328+
new_class.model_fields[f_name] = field
1329+
13231330
if meta and meta != DefaultMeta and meta != base_meta:
13241331
new_class.Meta = meta
13251332
new_class._meta = meta
@@ -1455,7 +1462,16 @@ class Config:
14551462

14561463
def __init__(__pydantic_self__, **data: Any) -> None:
14571464
__pydantic_self__.validate_primary_key()
1458-
super().__init__(**data)
1465+
missing_fields = __pydantic_self__.model_fields.keys() - data.keys() - {"pk"}
1466+
1467+
kwargs = data.copy()
1468+
1469+
# This is a hack, we need to manually make sure we are setting up defaults correctly when we encounter them
1470+
# because inheritance apparently won't cover that in pydantic 2.0.
1471+
for field in missing_fields:
1472+
default_value = __pydantic_self__.model_fields.get(field).default # type: ignore
1473+
kwargs[field] = default_value
1474+
super().__init__(**kwargs)
14591475

14601476
def __lt__(self, other):
14611477
"""Default sort: compare primary key of models."""
@@ -1527,7 +1543,7 @@ def validate_primary_key(cls):
15271543
else:
15281544
field_info = field.field_info
15291545

1530-
if getattr(field_info, "primary_key", None):
1546+
if getattr(field_info, "primary_key", None) is True:
15311547
primary_keys += 1
15321548
if primary_keys == 0:
15331549
raise RedisModelError("You must define a primary key for the model")
@@ -1808,7 +1824,7 @@ def schema_for_fields(cls):
18081824
else:
18091825
field_info = field.field_info
18101826

1811-
if getattr(field_info, "primary_key", None):
1827+
if getattr(field_info, "primary_key", None) is True:
18121828
if issubclass(_type, str):
18131829
redisearch_field = (
18141830
f"{name} TAG SEPARATOR {SINGLE_VALUE_TAG_FIELD_SEPARATOR}"
@@ -2003,7 +2019,7 @@ def schema_for_fields(cls):
20032019
field_info = field.field_info
20042020
else:
20052021
field_info = field
2006-
if getattr(field_info, "primary_key", None):
2022+
if getattr(field_info, "primary_key", None) is True:
20072023
if issubclass(_type, str):
20082024
redisearch_field = f"$.{name} AS {name} TAG SEPARATOR {SINGLE_VALUE_TAG_FIELD_SEPARATOR}"
20092025
else:

docs/images/logo.svg

Lines changed: 1 addition & 0 deletions
Loading

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "redis-om"
3-
version = "0.3.3"
3+
version = "0.3.5"
44
description = "Object mappings, and more, for Redis."
55
authors = ["Redis OSS <[email protected]>"]
66
maintainers = ["Redis OSS <[email protected]>"]
@@ -44,7 +44,7 @@ python-ulid = "^1.0.3"
4444
typing-extensions = "^4.4.0"
4545
hiredis = ">=2.2.3,<4.0.0"
4646
more-itertools = ">=8.14,<11.0"
47-
setuptools = {version = ">=70.0,<76.0", markers = "python_version >= '3.12'"}
47+
setuptools = ">=70.0"
4848

4949
[tool.poetry.dev-dependencies]
5050
mypy = "^1.9.0"

tests/test_hash_model.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
Migrator,
1919
NotFoundError,
2020
QueryNotSupportedError,
21+
RedisModel,
2122
RedisModelError,
2223
)
2324

@@ -951,3 +952,53 @@ class TestLiterals(HashModel):
951952
await item.save()
952953
rematerialized = await TestLiterals.find(TestLiterals.flavor == "pumpkin").first()
953954
assert rematerialized.pk == item.pk
955+
956+
957+
@py_test_mark_asyncio
958+
async def test_child_class_expression_proxy():
959+
# https://github.com/redis/redis-om-python/issues/669 seeing weird issue with child classes initalizing all their undefined members as ExpressionProxies
960+
class Model(HashModel):
961+
first_name: str
962+
last_name: str
963+
age: int = Field(default=18)
964+
bio: Optional[str] = Field(default=None)
965+
966+
class Child(Model):
967+
other_name: str
968+
# is_new: bool = Field(default=True)
969+
970+
await Migrator().run()
971+
m = Child(first_name="Steve", last_name="Lorello", other_name="foo")
972+
await m.save()
973+
print(m.age)
974+
assert m.age == 18
975+
976+
rematerialized = await Child.find(Child.pk == m.pk).first()
977+
978+
assert rematerialized.age == 18
979+
assert rematerialized.bio is None
980+
981+
982+
@py_test_mark_asyncio
983+
async def test_child_class_expression_proxy_with_mixin():
984+
# https://github.com/redis/redis-om-python/issues/669 seeing weird issue with child classes initalizing all their undefined members as ExpressionProxies
985+
class Model(RedisModel, abc.ABC):
986+
first_name: str
987+
last_name: str
988+
age: int = Field(default=18)
989+
bio: Optional[str] = Field(default=None)
990+
991+
class Child(Model, HashModel):
992+
other_name: str
993+
# is_new: bool = Field(default=True)
994+
995+
await Migrator().run()
996+
m = Child(first_name="Steve", last_name="Lorello", other_name="foo")
997+
await m.save()
998+
print(m.age)
999+
assert m.age == 18
1000+
1001+
rematerialized = await Child.find(Child.pk == m.pk).first()
1002+
1003+
assert rematerialized.age == 18
1004+
assert rematerialized.bio is None

tests/test_json_model.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
import pytest
1414
import pytest_asyncio
15+
from more_itertools.more import first
1516

1617
from aredis_om import (
1718
EmbeddedJsonModel,
@@ -20,6 +21,7 @@
2021
Migrator,
2122
NotFoundError,
2223
QueryNotSupportedError,
24+
RedisModel,
2325
RedisModelError,
2426
)
2527

@@ -1159,6 +1161,66 @@ class TestLiterals(JsonModel):
11591161
assert rematerialized.pk == item.pk
11601162

11611163

1164+
@py_test_mark_asyncio
1165+
async def test_two_false_pks():
1166+
from pydantic_core import PydanticUndefined as Undefined
1167+
1168+
class SomeModel(JsonModel):
1169+
field1: str = Field(index=True, primary_key=Undefined)
1170+
field2: str = Field(index=True, primary_key=Undefined)
1171+
1172+
SomeModel(field1="foo", field2="bar")
1173+
1174+
1175+
@py_test_mark_asyncio
1176+
async def test_child_class_expression_proxy():
1177+
# https://github.com/redis/redis-om-python/issues/669 seeing weird issue with child classes initalizing all their undefined members as ExpressionProxies
1178+
class Model(JsonModel):
1179+
first_name: str
1180+
last_name: str
1181+
age: int = Field(default=18)
1182+
bio: Optional[str] = Field(default=None)
1183+
1184+
class Child(Model):
1185+
is_new: bool = Field(default=True)
1186+
1187+
await Migrator().run()
1188+
m = Child(first_name="Steve", last_name="Lorello")
1189+
await m.save()
1190+
print(m.age)
1191+
assert m.age == 18
1192+
1193+
rematerialized = await Child.find(Child.pk == m.pk).first()
1194+
1195+
assert rematerialized.age == 18
1196+
assert rematerialized.age != 19
1197+
assert rematerialized.bio is None
1198+
1199+
1200+
@py_test_mark_asyncio
1201+
async def test_child_class_expression_proxy_with_mixin():
1202+
class Model(RedisModel, abc.ABC):
1203+
first_name: str
1204+
last_name: str
1205+
age: int = Field(default=18)
1206+
bio: Optional[str] = Field(default=None)
1207+
1208+
class Child(Model, JsonModel):
1209+
is_new: bool = Field(default=True)
1210+
1211+
await Migrator().run()
1212+
m = Child(first_name="Steve", last_name="Lorello")
1213+
await m.save()
1214+
print(m.age)
1215+
assert m.age == 18
1216+
1217+
rematerialized = await Child.find(Child.pk == m.pk).first()
1218+
1219+
assert rematerialized.age == 18
1220+
assert rematerialized.age != 19
1221+
assert rematerialized.bio is None
1222+
1223+
11621224
@py_test_mark_asyncio
11631225
async def test_merged_model_error():
11641226
class Player(EmbeddedJsonModel):

0 commit comments

Comments
 (0)