Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Can't use Pydantic validators with JsonModel #606

Closed
webdz9r opened this issue Apr 24, 2024 · 1 comment
Closed

Can't use Pydantic validators with JsonModel #606

webdz9r opened this issue Apr 24, 2024 · 1 comment

Comments

@webdz9r
Copy link

webdz9r commented Apr 24, 2024

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
@slorello89
Copy link
Member

This issue should be fixed in #603 - closing to tidy up, please let me know if you encounter this issue again.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants