diff --git a/docs/releases.md b/docs/releases.md index 0ea8d27..c94f72f 100644 --- a/docs/releases.md +++ b/docs/releases.md @@ -1,3 +1,15 @@ +# 0.10.23 + +## ✨ Features + +* Add ability to pass `comment` to sqlalchemy when creating a column [#485](https://github.com/collerek/ormar/issues/485) + +## 🐛 Fixes + +* Fix `LargeBinary` fields that can be nullable [#409](https://github.com/collerek/ormar/issues/409) +* Make `ormar` models pickable [#413](https://github.com/collerek/ormar/issues/413) + + # 0.10.22 ## 🐛 Fixes diff --git a/ormar/fields/base.py b/ormar/fields/base.py index 8d05cdf..f7eaff5 100644 --- a/ormar/fields/base.py +++ b/ormar/fields/base.py @@ -97,6 +97,8 @@ class BaseField(FieldInfo): self.ormar_default: Any = kwargs.pop("default", None) self.server_default: Any = kwargs.pop("server_default", None) + self.comment: str = kwargs.pop("comment", None) + self.represent_as_base64_str: bool = kwargs.pop( "represent_as_base64_str", False ) @@ -271,6 +273,7 @@ class BaseField(FieldInfo): unique=self.unique, default=self.ormar_default, server_default=self.server_default, + comment=self.comment, ) else: column = self._get_encrypted_column(name=name) diff --git a/ormar/models/newbasemodel.py b/ormar/models/newbasemodel.py index 599ccdf..eb0805c 100644 --- a/ormar/models/newbasemodel.py +++ b/ormar/models/newbasemodel.py @@ -195,6 +195,29 @@ class NewBaseModel(pydantic.BaseModel, ModelTableProxy, metaclass=ModelMetaclass """ return super().__getattribute__(item) + def __getstate__(self) -> Dict[Any, Any]: + state = super().__getstate__() + self_dict = self.dict() + state["__dict__"].update(**self_dict) + return state + + def __setstate__(self, state: Dict[Any, Any]) -> None: + relations = { + k: v + for k, v in state["__dict__"].items() + if k in self.extract_related_names() + } + basic_state = { + k: v + for k, v in state["__dict__"].items() + if k not in self.extract_related_names() + } + state["__dict__"] = basic_state + super().__setstate__(state) + self._initialize_internal_attributes() + for name, value in relations.items(): + setattr(self, name, value) + def _internal_set(self, name: str, value: Any) -> None: """ Delegates call to pydantic. diff --git a/tests/test_model_definition/test_models_are_pickable.py b/tests/test_model_definition/test_models_are_pickable.py new file mode 100644 index 0000000..f094b48 --- /dev/null +++ b/tests/test_model_definition/test_models_are_pickable.py @@ -0,0 +1,60 @@ +import pickle +from typing import Optional + +import databases +import pytest +import sqlalchemy + +import ormar +from tests.settings import DATABASE_URL + +database = databases.Database(DATABASE_URL, force_rollback=True) +metadata = sqlalchemy.MetaData() + + +class User(ormar.Model): + class Meta: + tablename = "users" + metadata = metadata + database = database + + id: int = ormar.Integer(primary_key=True) + name: str = ormar.String(max_length=100) + properties = ormar.JSON(nullable=True) + + +class Post(ormar.Model): + class Meta: + tablename = "posts" + metadata = metadata + database = database + + id: int = ormar.Integer(primary_key=True) + name: str = ormar.String(max_length=100) + created_by: Optional[User] = ormar.ForeignKey(User) + + +@pytest.fixture(autouse=True, scope="module") +def create_test_database(): + engine = sqlalchemy.create_engine(DATABASE_URL) + metadata.drop_all(engine) + metadata.create_all(engine) + yield + metadata.drop_all(engine) + + +@pytest.mark.asyncio +async def test_dumping_and_loading_model_works(): + user = await User(name="Test", properties={"aa": "bb"}).save() + post = Post(name="Test post") + await user.posts.add(post) + pickled_value = pickle.dumps(user) + python_value = pickle.loads(pickled_value) + assert isinstance(python_value, User) + assert python_value.name == "Test" + assert python_value.properties == {"aa": "bb"} + assert python_value.posts[0].name == "Test post" + await python_value.load() + await python_value.update(name="Test2") + check = await User.objects.get() + assert check.name == "Test2" diff --git a/tests/test_model_definition/test_setting_comments_in_db.py b/tests/test_model_definition/test_setting_comments_in_db.py new file mode 100644 index 0000000..588cdf1 --- /dev/null +++ b/tests/test_model_definition/test_setting_comments_in_db.py @@ -0,0 +1,36 @@ +import databases +import pytest +import sqlalchemy + +import ormar +from ormar.models import Model +from tests.settings import DATABASE_URL + +metadata = sqlalchemy.MetaData() + +database = databases.Database(DATABASE_URL, force_rollback=True) + + +class Comment(Model): + class Meta(ormar.ModelMeta): + tablename = "comments" + metadata = metadata + database = database + + test: int = ormar.Integer(primary_key=True, comment="primary key of comments") + test_string: str = ormar.String(max_length=250, comment="test that it works") + + +@pytest.fixture(autouse=True, scope="module") +def create_test_database(): + engine = sqlalchemy.create_engine(DATABASE_URL) + metadata.create_all(engine) + yield + metadata.drop_all(engine) + + +@pytest.mark.asyncio +async def test_comments_are_set_in_db(): + columns = Comment.Meta.table.c + for c in columns: + assert c.comment == Comment.Meta.model_fields[c.name].comment