Skip to content

Commit

Permalink
Refactoring Redis OM Python to use pydantic 2.0 types and validators (#…
Browse files Browse the repository at this point in the history
…603)

* refactoring model to use pydantic 2.0 types and validators

* adding tests for #591

* adding tests with uuid

* readme fixes

* fixing typo in NOT_IN
  • Loading branch information
slorello89 authored May 2, 2024
1 parent 78e9a39 commit f1ed5b2
Show file tree
Hide file tree
Showing 9 changed files with 468 additions and 97 deletions.
18 changes: 9 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ Check out this example of modeling customer data with Redis OM. First, we create
import datetime
from typing import Optional

from pydantic.v1 import EmailStr
from pydantic import EmailStr

from redis_om import HashModel

Expand All @@ -104,7 +104,7 @@ class Customer(HashModel):
email: EmailStr
join_date: datetime.date
age: int
bio: Optional[str]
bio: Optional[str] = None
```

Now that we have a `Customer` model, let's use it to save customer data to Redis.
Expand All @@ -113,7 +113,7 @@ Now that we have a `Customer` model, let's use it to save customer data to Redis
import datetime
from typing import Optional

from pydantic.v1 import EmailStr
from pydantic import EmailStr

from redis_om import HashModel

Expand All @@ -124,7 +124,7 @@ class Customer(HashModel):
email: EmailStr
join_date: datetime.date
age: int
bio: Optional[str]
bio: Optional[str] = None


# First, we create a new `Customer` object:
Expand Down Expand Up @@ -168,7 +168,7 @@ For example, because we used the `EmailStr` type for the `email` field, we'll ge
import datetime
from typing import Optional

from pydantic.v1 import EmailStr, ValidationError
from pydantic import EmailStr, ValidationError

from redis_om import HashModel

Expand All @@ -179,7 +179,7 @@ class Customer(HashModel):
email: EmailStr
join_date: datetime.date
age: int
bio: Optional[str]
bio: Optional[str] = None


try:
Expand Down Expand Up @@ -222,7 +222,7 @@ To show how this works, we'll make a small change to the `Customer` model we def
import datetime
from typing import Optional

from pydantic.v1 import EmailStr
from pydantic import EmailStr

from redis_om import (
Field,
Expand All @@ -237,7 +237,7 @@ class Customer(HashModel):
email: EmailStr
join_date: datetime.date
age: int = Field(index=True)
bio: Optional[str]
bio: Optional[str] = None


# Now, if we use this model with a Redis deployment that has the
Expand Down Expand Up @@ -287,7 +287,7 @@ from redis_om import (

class Address(EmbeddedJsonModel):
address_line_1: str
address_line_2: Optional[str]
address_line_2: Optional[str] = None
city: str = Field(index=True)
state: str = Field(index=True)
country: str
Expand Down
90 changes: 85 additions & 5 deletions aredis_om/_compat.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,99 @@
from dataclasses import dataclass, is_dataclass
from typing import (
Any,
Callable,
Deque,
Dict,
FrozenSet,
List,
Mapping,
Sequence,
Set,
Tuple,
Type,
Union,
)

from pydantic.version import VERSION as PYDANTIC_VERSION
from typing_extensions import Annotated, Literal, get_args, get_origin


PYDANTIC_V2 = PYDANTIC_VERSION.startswith("2.")

if PYDANTIC_V2:
from pydantic.v1 import BaseModel, validator
from pydantic.v1.fields import FieldInfo, ModelField, Undefined, UndefinedType
from pydantic.v1.json import ENCODERS_BY_TYPE
from pydantic.v1.main import ModelMetaclass, validate_model

def use_pydantic_2_plus():
return True

from pydantic import BaseModel, TypeAdapter
from pydantic import ValidationError as ValidationError
from pydantic import validator
from pydantic._internal._model_construction import ModelMetaclass
from pydantic._internal._repr import Representation
from pydantic.deprecated.json import ENCODERS_BY_TYPE
from pydantic.fields import FieldInfo
from pydantic.v1.main import validate_model
from pydantic.v1.typing import NoArgAnyCallable
from pydantic.v1.utils import Representation
from pydantic_core import PydanticUndefined as Undefined
from pydantic_core import PydanticUndefinedType as UndefinedType

@dataclass
class ModelField:
field_info: FieldInfo
name: str
mode: Literal["validation", "serialization"] = "validation"

@property
def alias(self) -> str:
a = self.field_info.alias
return a if a is not None else self.name

@property
def required(self) -> bool:
return self.field_info.is_required()

@property
def default(self) -> Any:
return self.get_default()

@property
def type_(self) -> Any:
return self.field_info.annotation

def __post_init__(self) -> None:
self._type_adapter: TypeAdapter[Any] = TypeAdapter(
Annotated[self.field_info.annotation, self.field_info]
)

def get_default(self) -> Any:
if self.field_info.is_required():
return Undefined
return self.field_info.get_default(call_default_factory=True)

def validate(
self,
value: Any,
values: Dict[str, Any] = {}, # noqa: B006
*,
loc: Tuple[Union[int, str], ...] = (),
) -> Tuple[Any, Union[List[Dict[str, Any]], None]]:
return (
self._type_adapter.validate_python(value, from_attributes=True),
None,
)

def __hash__(self) -> int:
# Each ModelField is unique for our purposes, to allow making a dict from
# ModelField to its JSON Schema.
return id(self)

else:
from pydantic import BaseModel, validator
from pydantic.fields import FieldInfo, ModelField, Undefined, UndefinedType
from pydantic.json import ENCODERS_BY_TYPE
from pydantic.main import ModelMetaclass, validate_model
from pydantic.typing import NoArgAnyCallable
from pydantic.utils import Representation

def use_pydantic_2_plus():
return False
2 changes: 1 addition & 1 deletion aredis_om/model/encoders.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ def jsonable_encoder(
if exclude is not None and not isinstance(exclude, (set, dict)):
exclude = set(exclude)

if isinstance(obj, BaseModel):
if isinstance(obj, BaseModel) and hasattr(obj, "__config__"):
encoder = getattr(obj.__config__, "json_encoders", {})
if custom_encoder:
encoder.update(custom_encoder)
Expand Down
Loading

0 comments on commit f1ed5b2

Please sign in to comment.