Skip to content

Can't use Pydantic validators with JsonModel #606

Closed
@webdz9r

Description

@webdz9r

I'm attempting to hash the password before save using Pydantic validators
sample Model code


from pydantic import BaseModel, Field
import datetime
from redis_om import (EmbeddedJsonModel, Field, JsonModel)
from pydantic import PositiveInt
from typing import Optional, List

from werkzeug.security import generate_password_hash, check_password_hash
from pydantic import validator


class User(JsonModel):
    first_name: str = Field(index=True)
    last_name: str = Field(index=True)
    email: str = Field(index=True)
    mobile: str = Field(index=True)
    created_at: datetime.datetime = Field()
    updated_at: datetime.datetime = Field()
    timezone: Optional[str]
    courses: set[CourseObj.pk] = Field(default=[])
    meta: Optional[dict] = Field(default={})

    password: str = Field()
    status: str = Field()
    role: str = Field() # admin, user

    @validator('password', pre=True, always=True)
    def hash_password(cls, password):
        return generate_password_hash(password)
    
    def to_json(self) -> dict:
        
        return {
            "id": self.pk,
            "first_name": self.first_name,
            "last_name": self.last_name,
            "name": f"{self.first_name} {self.last_name}",
            "email": self.email,
            "mobile": self.mobile,
            "created_at": self.created_at.strftime('%m-%d-%Y'),
            "updated_at": self.updated_at.strftime('%m-%d-%Y'),
            "timezone": self.timezone,
        }
        

When I import my code it returns TypeError: cannot pickle 'classmethod' object

full stack

File "/Users/chris/.pyenv/versions/3.11.2/lib/python3.11/site-packages/redis_om/model/model.py", line 1205, in __new__
    new_class = super().__new__(cls, name, bases, attrs, **kwargs)
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/chris/.pyenv/versions/3.11.2/lib/python3.11/site-packages/pydantic/v1/main.py", line 221, in __new__
    inferred = ModelField.infer(
               ^^^^^^^^^^^^^^^^^
  File "/Users/chris/.pyenv/versions/3.11.2/lib/python3.11/site-packages/pydantic/v1/fields.py", line 506, in infer
    return cls(
           ^^^^
  File "/Users/chris/.pyenv/versions/3.11.2/lib/python3.11/site-packages/pydantic/v1/fields.py", line 436, in __init__
    self.prepare()
  File "/Users/chris/.pyenv/versions/3.11.2/lib/python3.11/site-packages/pydantic/v1/fields.py", line 546, in prepare
    self._set_default_and_type()
  File "/Users/chris/.pyenv/versions/3.11.2/lib/python3.11/site-packages/pydantic/v1/fields.py", line 570, in _set_default_and_type
    default_value = self.get_default()
                    ^^^^^^^^^^^^^^^^^^
  File "/Users/chris/.pyenv/versions/3.11.2/lib/python3.11/site-packages/pydantic/v1/fields.py", line 439, in get_default
    return smart_deepcopy(self.default) if self.default_factory is None else self.default_factory()
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/chris/.pyenv/versions/3.11.2/lib/python3.11/site-packages/pydantic/v1/utils.py", line 693, in smart_deepcopy
    return deepcopy(obj)  # slowest way when we actually might need a deepcopy
           ^^^^^^^^^^^^^
  File "/Users/chris/.pyenv/versions/3.11.2/lib/python3.11/copy.py", line 172, in deepcopy
    y = _reconstruct(x, memo, *rv)
        ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/chris/.pyenv/versions/3.11.2/lib/python3.11/copy.py", line 271, in _reconstruct
    state = deepcopy(state, memo)
            ^^^^^^^^^^^^^^^^^^^^^
  File "/Users/chris/.pyenv/versions/3.11.2/lib/python3.11/copy.py", line 146, in deepcopy
    y = copier(x, memo)
        ^^^^^^^^^^^^^^^
  File "/Users/chris/.pyenv/versions/3.11.2/lib/python3.11/copy.py", line 231, in _deepcopy_dict
    y[deepcopy(key, memo)] = deepcopy(value, memo)
                             ^^^^^^^^^^^^^^^^^^^^^
  File "/Users/chris/.pyenv/versions/3.11.2/lib/python3.11/copy.py", line 161, in deepcopy
    rv = reductor(4)
         ^^^^^^^^^^^
TypeError: cannot pickle 'classmethod' object

When I change to to BaseModel the code works fine

class User(BaseModel):
    first_name: str = Field(index=True)
    last_name: str = Field(index=True)
    ....

Version details

pydantic==2.4.2
pydantic_core==2.10.1
redis==5.0.3
redis-om==0.2.2

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions