|
7 | 7 |
|
8 | 8 | import pytest |
9 | 9 | import sqlalchemy |
10 | | -from sqlalchemy import func |
11 | | -from sqlalchemy.orm import DeclarativeBase, Mapped, MappedAsDataclass, declared_attr, mapped_column |
| 10 | +from sqlalchemy import ForeignKey, func |
| 11 | +from sqlalchemy.orm import DeclarativeBase, Mapped, MappedAsDataclass, declared_attr, mapped_column, relationship |
12 | 12 | from typing_extensions import Annotated |
13 | 13 |
|
14 | | -from litestar.contrib.sqlalchemy.dto import SQLAlchemyDTO |
| 14 | +from litestar.contrib.sqlalchemy.dto import SQLAlchemyDTO, parse_type_from_element |
15 | 15 | from litestar.dto.factory import DTOConfig, DTOField, Mark |
16 | 16 | from litestar.dto.factory.field import DTO_FIELD_META_KEY |
17 | 17 | from litestar.dto.interface import ConnectionContext, HandlerContext |
@@ -553,10 +553,76 @@ class A(Base): |
553 | 553 | assert vars(model)["c"] == [1, 2, 3] |
554 | 554 |
|
555 | 555 |
|
556 | | -async def test_no_type_hints(base: type[DeclarativeBase], connection_context: ConnectionContext) -> None: |
| 556 | +async def test_no_type_hint_column(base: type[DeclarativeBase], connection_context: ConnectionContext) -> None: |
557 | 557 | class Model(base): |
558 | | - field = mapped_column(sqlalchemy.String) |
| 558 | + nullable_field = mapped_column(sqlalchemy.String) |
| 559 | + not_nullable_field = mapped_column(sqlalchemy.String, nullable=False, default="") |
559 | 560 |
|
560 | 561 | dto_type = SQLAlchemyDTO[Annotated[Model, DTOConfig()]] |
561 | | - with pytest.raises(ImproperlyConfiguredException, match="No type information found for 'Model.field'"): |
562 | | - await get_model_from_dto(dto_type, Model, connection_context, b"") |
| 562 | + model = await get_model_from_dto(dto_type, Model, connection_context, b"{}") |
| 563 | + assert model.nullable_field is None |
| 564 | + assert model.not_nullable_field == "" |
| 565 | + |
| 566 | + |
| 567 | +async def test_no_type_hint_scalar_relationship_with_nullable_fk( |
| 568 | + base: type[DeclarativeBase], connection_context: ConnectionContext |
| 569 | +) -> None: |
| 570 | + class Child(base): |
| 571 | + ... |
| 572 | + |
| 573 | + class Model(base): |
| 574 | + child_id = mapped_column(ForeignKey("child.id")) |
| 575 | + child = relationship(Child) |
| 576 | + |
| 577 | + dto_type = SQLAlchemyDTO[Annotated[Model, DTOConfig(exclude={"child_id"})]] |
| 578 | + model = await get_model_from_dto(dto_type, Model, connection_context, b"{}") |
| 579 | + assert model.child is None |
| 580 | + |
| 581 | + |
| 582 | +async def test_no_type_hint_scalar_relationship_with_not_nullable_fk( |
| 583 | + base: type[DeclarativeBase], connection_context: ConnectionContext |
| 584 | +) -> None: |
| 585 | + class Child(base): |
| 586 | + ... |
| 587 | + |
| 588 | + class Model(base): |
| 589 | + child_id = mapped_column(ForeignKey("child.id"), nullable=False) |
| 590 | + child = relationship(Child) |
| 591 | + |
| 592 | + dto_type = SQLAlchemyDTO[Annotated[Model, DTOConfig(exclude={"child_id"})]] |
| 593 | + model = await get_model_from_dto(dto_type, Model, connection_context, b'{"child": {}}') |
| 594 | + assert isinstance(model.child, Child) |
| 595 | + |
| 596 | + |
| 597 | +async def test_no_type_hint_collection_relationship( |
| 598 | + base: type[DeclarativeBase], connection_context: ConnectionContext |
| 599 | +) -> None: |
| 600 | + class Child(base): |
| 601 | + model_id = mapped_column(ForeignKey("model.id")) |
| 602 | + |
| 603 | + class Model(base): |
| 604 | + children = relationship(Child) |
| 605 | + |
| 606 | + dto_type = SQLAlchemyDTO[Annotated[Model, DTOConfig()]] |
| 607 | + model = await get_model_from_dto(dto_type, Model, connection_context, b'{"children": []}') |
| 608 | + assert model.children == [] |
| 609 | + |
| 610 | + |
| 611 | +async def test_no_type_hint_collection_relationship_alt_collection_class( |
| 612 | + base: type[DeclarativeBase], connection_context: ConnectionContext |
| 613 | +) -> None: |
| 614 | + class Child(base): |
| 615 | + model_id = mapped_column(ForeignKey("model.id")) |
| 616 | + |
| 617 | + class Model(base): |
| 618 | + children = relationship(Child, collection_class=set) |
| 619 | + |
| 620 | + dto_type = SQLAlchemyDTO[Annotated[Model, DTOConfig()]] |
| 621 | + model = await get_model_from_dto(dto_type, Model, connection_context, b'{"children": []}') |
| 622 | + assert model.children == set() |
| 623 | + |
| 624 | + |
| 625 | +def test_parse_type_from_element_failure() -> None: |
| 626 | + with pytest.raises(ImproperlyConfiguredException) as exc: |
| 627 | + parse_type_from_element(1) |
| 628 | + assert str(exc.value) == "500: Unable to parse type from element '1'. Consider adding a type hint." |
0 commit comments