Skip to content

Commit 34ca489

Browse files
committed
merge
2 parents 7238e99 + 6fb2a73 commit 34ca489

File tree

4 files changed

+86
-2
lines changed

4 files changed

+86
-2
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ sync: $(INSTALL_STAMP)
5353
lint: $(INSTALL_STAMP) dist
5454
$(POETRY) run isort --profile=black --lines-after-imports=2 ./tests/ $(NAME) $(SYNC_NAME)
5555
$(POETRY) run black ./tests/ $(NAME)
56-
$(POETRY) run flake8 --ignore=W503,E501,F401,E731 ./tests/ $(NAME) $(SYNC_NAME)
56+
$(POETRY) run flake8 --ignore=W503,E501,F401,E731,E712 ./tests/ $(NAME) $(SYNC_NAME)
5757
$(POETRY) run mypy ./tests/ $(NAME) $(SYNC_NAME) --ignore-missing-imports --exclude migrate.py --exclude _compat\.py$
5858
$(POETRY) run bandit -r $(NAME) $(SYNC_NAME) -s B608
5959

aredis_om/model/model.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@ def get_outer_type(field):
8383
field.annotation
8484
):
8585
return field.annotation
86+
elif not hasattr(field.annotation, "__args__"):
87+
return None
8688
else:
8789
return field.annotation.__args__[0]
8890

@@ -116,6 +118,8 @@ class Operators(Enum):
116118
STARTSWITH = 14
117119
ENDSWITH = 15
118120
CONTAINS = 16
121+
TRUE = 17
122+
FALSE = 18
119123

120124
def __str__(self):
121125
return str(self.name)
@@ -582,6 +586,8 @@ def resolve_field_type(
582586
"Only lists and tuples are supported for multi-value fields. "
583587
f"Docs: {ERRORS_URL}#E4"
584588
)
589+
elif field_type is bool:
590+
return RediSearchFieldTypes.TAG
585591
elif any(issubclass(field_type, t) for t in NUMERIC_TYPES):
586592
# Index numeric Python types as NUMERIC fields, so we can support
587593
# range queries.
@@ -676,7 +682,11 @@ def resolve_value(
676682
separator_char,
677683
)
678684
return ""
679-
if isinstance(value, int):
685+
if isinstance(value, bool):
686+
result = "@{field_name}:{{{value}}}".format(
687+
field_name=field_name, value=value
688+
)
689+
elif isinstance(value, int):
680690
# This if will hit only if the field is a primary key of type int
681691
result = f"@{field_name}:[{value} {value}]"
682692
elif separator_char in value:
@@ -1819,6 +1829,8 @@ def schema_for_type(cls, name, typ: Any, field_info: PydanticFieldInfo):
18191829
return ""
18201830
embedded_cls = embedded_cls[0]
18211831
schema = cls.schema_for_type(name, embedded_cls, field_info)
1832+
elif typ is bool:
1833+
schema = f"{name} TAG"
18221834
elif any(issubclass(typ, t) for t in NUMERIC_TYPES):
18231835
vector_options: Optional[VectorFieldOptions] = getattr(
18241836
field_info, "vector_options", None
@@ -1948,6 +1960,9 @@ def schema_for_fields(cls):
19481960

19491961
for name, field in fields.items():
19501962
_type = get_outer_type(field)
1963+
if _type is None:
1964+
continue
1965+
19511966
if (
19521967
not isinstance(field, FieldInfo)
19531968
and hasattr(field, "metadata")
@@ -2126,6 +2141,8 @@ def schema_for_type(
21262141
raise sortable_tag_error
21272142
if case_sensitive is True:
21282143
schema += " CASESENSITIVE"
2144+
elif typ is bool:
2145+
schema = f"{path} AS {index_field_name} TAG"
21292146
elif any(issubclass(typ, t) for t in NUMERIC_TYPES):
21302147
schema = f"{path} AS {index_field_name} NUMERIC"
21312148
elif issubclass(typ, str):

docs/getting_started.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -702,6 +702,27 @@ Customer.find((Customer.last_name == "Brookins") | (
702702
) & (Customer.last_name == "Smith")).all()
703703
```
704704

705+
### Saving and querying Boolean values
706+
707+
For historical reasons, saving and querying Boolean values is not supported in `HashModels`, however in JSON models,
708+
you may store and query Boolean values using the `==` syntax:
709+
710+
```python
711+
from redis_om import (
712+
Field,
713+
JsonModel,
714+
Migrator
715+
)
716+
717+
class Demo(JsonModel):
718+
b: bool = Field(index=True)
719+
720+
Migrator().run()
721+
d = Demo(b=True)
722+
d.save()
723+
res = Demo.find(Demo.b == True)
724+
```
725+
705726
## Calling Other Redis Commands
706727

707728
Sometimes you'll need to run a Redis command directly. Redis OM supports this through the `db` method on your model's class. This returns a connected Redis client instance which exposes a function named for each Redis command. For example, let's perform some basic set operations:

tests/test_json_model.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1003,3 +1003,49 @@ class TestUpdatesClass(JsonModel):
10031003

10041004
rematerialized = await TestUpdatesClass.find(TestUpdatesClass.pk == t.pk).first()
10051005
assert rematerialized.age == 42
1006+
1007+
1008+
async def test_model_with_dict():
1009+
class EmbeddedJsonModelWithDict(EmbeddedJsonModel):
1010+
dict: Dict
1011+
1012+
class ModelWithDict(JsonModel):
1013+
embedded_model: EmbeddedJsonModelWithDict
1014+
info: Dict
1015+
1016+
await Migrator().run()
1017+
d = dict()
1018+
inner_dict = dict()
1019+
d["foo"] = "bar"
1020+
inner_dict["bar"] = "foo"
1021+
embedded_model = EmbeddedJsonModelWithDict(dict=inner_dict)
1022+
item = ModelWithDict(info=d, embedded_model=embedded_model)
1023+
await item.save()
1024+
1025+
rematerialized = await ModelWithDict.find(ModelWithDict.pk == item.pk).first()
1026+
assert rematerialized.pk == item.pk
1027+
assert rematerialized.info["foo"] == "bar"
1028+
assert rematerialized.embedded_model.dict["bar"] == "foo"
1029+
1030+
1031+
@py_test_mark_asyncio
1032+
async def test_boolean():
1033+
class Example(JsonModel):
1034+
b: bool = Field(index=True)
1035+
d: datetime.date = Field(index=True)
1036+
name: str = Field(index=True)
1037+
1038+
await Migrator().run()
1039+
1040+
ex = Example(b=True, name="steve", d=datetime.date.today())
1041+
exFalse = Example(b=False, name="foo", d=datetime.date.today())
1042+
await ex.save()
1043+
await exFalse.save()
1044+
res = await Example.find(Example.b == True).first()
1045+
assert res.name == "steve"
1046+
1047+
res = await Example.find(Example.b == False).first()
1048+
assert res.name == "foo"
1049+
1050+
res = await Example.find(Example.d == ex.d and Example.b == True).first()
1051+
assert res.name == ex.name

0 commit comments

Comments
 (0)