From b423c1c56d3e44ba475f7ba8bf74cf9670d2d613 Mon Sep 17 00:00:00 2001 From: collerek Date: Wed, 21 Jul 2021 14:39:23 +0200 Subject: [PATCH] fixes for #270 and #274 --- docs/releases.md | 9 +++ ormar/__init__.py | 2 +- ormar/models/helpers/sqlalchemy.py | 2 +- ormar/models/mixins/save_mixin.py | 2 +- .../test_nested_models_pydantic.py | 23 ++---- ...oreign_key_value_used_for_related_model.py | 81 +++++++++++++++++++ 6 files changed, 100 insertions(+), 19 deletions(-) create mode 100644 tests/test_model_definition/test_foreign_key_value_used_for_related_model.py diff --git a/docs/releases.md b/docs/releases.md index 22523bd..48b0a0e 100644 --- a/docs/releases.md +++ b/docs/releases.md @@ -1,3 +1,12 @@ +# 0.10.15 + +## 🐛 Fixes + +* Fix generating pydantic models tree with nested models (by @pawamoy - thanks!) [#278](https://github.com/collerek/ormar/issues/278) +* Fix missing f-string in warning about missing primary key field [#274](https://github.com/collerek/ormar/issues/274) +* Fix passing foreign key value as relation (additional guard, fixed already in the latest release) [#270](https://github.com/collerek/ormar/issues/270) + + # 0.10.14 ## ✨ Features diff --git a/ormar/__init__.py b/ormar/__init__.py index 69929d3..c9f8bc8 100644 --- a/ormar/__init__.py +++ b/ormar/__init__.py @@ -76,7 +76,7 @@ class UndefinedType: # pragma no cover Undefined = UndefinedType() -__version__ = "0.10.14" +__version__ = "0.10.15" __all__ = [ "Integer", "BigInteger", diff --git a/ormar/models/helpers/sqlalchemy.py b/ormar/models/helpers/sqlalchemy.py index 113dd98..3ad53f1 100644 --- a/ormar/models/helpers/sqlalchemy.py +++ b/ormar/models/helpers/sqlalchemy.py @@ -152,7 +152,7 @@ def sqlalchemy_columns_from_model_fields( if len(model_fields.keys()) == 0: model_fields["id"] = ormar.Integer(name="id", primary_key=True) logging.warning( - "Table {table_name} had no fields so auto " + f"Table {new_model.Meta.tablename} had no fields so auto " "Integer primary key named `id` created." ) validate_related_names_in_relations(model_fields, new_model) diff --git a/ormar/models/mixins/save_mixin.py b/ormar/models/mixins/save_mixin.py index f96c3b9..b70ce6d 100644 --- a/ormar/models/mixins/save_mixin.py +++ b/ormar/models/mixins/save_mixin.py @@ -121,7 +121,7 @@ class SavePrepareMixin(RelationMixin, AliasMixin): f"model without pk set!" ) model_dict[field] = pk_value - elif field_value: # nested dict + elif isinstance(field_value, (list, dict)) and field_value: if isinstance(field_value, list): model_dict[field] = [ target.get(target_pkname) for target in field_value diff --git a/tests/test_inheritance_and_pydantic_generation/test_nested_models_pydantic.py b/tests/test_inheritance_and_pydantic_generation/test_nested_models_pydantic.py index ccdf3b0..a96191c 100644 --- a/tests/test_inheritance_and_pydantic_generation/test_nested_models_pydantic.py +++ b/tests/test_inheritance_and_pydantic_generation/test_nested_models_pydantic.py @@ -26,8 +26,7 @@ class Package(ormar.Model): pass id: int = ormar.Integer(primary_key=True) - library: Library = ormar.ForeignKey(Library, - related_name="packages") + library: Library = ormar.ForeignKey(Library, related_name="packages") version: str = ormar.String(max_length=100) @@ -47,34 +46,26 @@ class TicketPackage(ormar.Model): id: int = ormar.Integer(primary_key=True) status: str = ormar.String(max_length=100) ticket: Ticket = ormar.ForeignKey(Ticket, related_name="packages") - package: Package = ormar.ForeignKey(Package, - related_name="tickets") + package: Package = ormar.ForeignKey(Package, related_name="tickets") def test_have_proper_children(): TicketPackageOut = TicketPackage.get_pydantic(exclude={"ticket"}) - assert 'package' in TicketPackageOut.__fields__ + assert "package" in TicketPackageOut.__fields__ PydanticPackage = TicketPackageOut.__fields__["package"].type_ - assert 'library' in PydanticPackage.__fields__ + assert "library" in PydanticPackage.__fields__ def test_casts_properly(): payload = { "id": 0, "status": "string", - "ticket": { - "id": 0, - "number": 0, - "status": "string" - }, + "ticket": {"id": 0, "number": 0, "status": "string"}, "package": { "version": "string", "id": 0, - "library": { - "id": 0, - "name": "string" - } - } + "library": {"id": 0, "name": "string"}, + }, } test_package = TicketPackage(**payload) TicketPackageOut = TicketPackage.get_pydantic(exclude={"ticket"}) diff --git a/tests/test_model_definition/test_foreign_key_value_used_for_related_model.py b/tests/test_model_definition/test_foreign_key_value_used_for_related_model.py new file mode 100644 index 0000000..cc24cb4 --- /dev/null +++ b/tests/test_model_definition/test_foreign_key_value_used_for_related_model.py @@ -0,0 +1,81 @@ +import uuid +from typing import List, 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 BaseMeta(ormar.ModelMeta): + metadata = metadata + database = database + + +class PageLink(ormar.Model): + class Meta(BaseMeta): + tablename = "pagelinks" + + id: int = ormar.Integer(primary_key=True) + value: str = ormar.String(max_length=2048) + country: str = ormar.String(max_length=1000) + + +class Post(ormar.Model): + class Meta(BaseMeta): + tablename = "posts" + + id: int = ormar.Integer(primary_key=True) + title: str = ormar.String(max_length=500) + link: PageLink = ormar.ForeignKey( + PageLink, related_name="posts", ondelete="CASCADE" + ) + + +class Department(ormar.Model): + class Meta(BaseMeta): + pass + + id: uuid.UUID = ormar.UUID(primary_key=True, default=uuid.uuid4()) + name: str = ormar.String(max_length=100) + + +class Course(ormar.Model): + class Meta(BaseMeta): + pass + + id: int = ormar.Integer(primary_key=True) + name: str = ormar.String(max_length=100) + completed: bool = ormar.Boolean(default=False) + department: Optional[Department] = ormar.ForeignKey(Department) + + +@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_pass_int_values_as_fk(): + async with database: + async with database.transaction(force_rollback=True): + link = await PageLink(id=1, value="test", country="USA").save() + await Post.objects.create(title="My post", link=link.id) + post_check = await Post.objects.select_related("link").get() + assert post_check.link == link + + +@pytest.mark.asyncio +async def test_pass_uuid_value_as_fk(): + async with database: + async with database.transaction(force_rollback=True): + dept = await Department(name="Department test").save() + await Course(name="Test course", department=dept.id).save()