|
18 | 18 |
|
19 | 19 | from datetime import date, datetime, time, timezone |
20 | 20 | from decimal import Decimal |
21 | | -from typing import Any |
| 21 | +from typing import Any, List, Optional |
22 | 22 |
|
23 | 23 | import pytest |
| 24 | +from sqlalchemy import (JSON, BigInteger, Boolean, Date, DateTime, Float, |
| 25 | + ForeignKey, Numeric, SmallInteger, String, Text, Time, |
| 26 | + and_, create_engine, or_, text) |
| 27 | +from sqlalchemy.orm import (DeclarativeBase, Mapped, Session, joinedload, |
| 28 | + mapped_column, relationship, sessionmaker, |
| 29 | + subqueryload) |
24 | 30 | from sqlalchemy.sql import func |
25 | | -from sqlalchemy.orm import ( |
26 | | - declarative_base, sessionmaker, relationship, Session, joinedload, |
27 | | - subqueryload |
28 | | -) |
29 | | -from sqlalchemy import ( |
30 | | - create_engine, Column, ForeignKey, Integer, BigInteger, SmallInteger, |
31 | | - Float, Numeric, String, Boolean, Date, Time, DateTime, Text, JSON, or_, |
32 | | - and_, text |
33 | | -) |
34 | 31 |
|
35 | 32 | from tests.integration.container.utils.rds_test_utility import RdsTestUtility |
36 | 33 | from ..utils.conditions import (disable_on_features, enable_on_deployments, |
|
40 | 37 | from ..utils.test_environment import TestEnvironment |
41 | 38 | from ..utils.test_environment_features import TestEnvironmentFeatures |
42 | 39 |
|
43 | | -class Base: |
44 | | - __allow_unmapped__ = True |
45 | 40 |
|
46 | | -Base = declarative_base(cls=Base) |
| 41 | +class Base(DeclarativeBase): |
| 42 | + pass |
| 43 | + |
47 | 44 |
|
48 | 45 | class TestModel(Base): |
49 | 46 | """Basic test model for SQLAlchemy ORM functionality""" |
50 | 47 | __tablename__ = 'sqlalchemy_test_model' |
51 | 48 |
|
52 | | - id = Column(Integer, primary_key=True) |
| 49 | + id: Mapped[int] = mapped_column(primary_key=True) |
| 50 | + name: Mapped[str] = mapped_column(String(100)) |
| 51 | + email: Mapped[str] = mapped_column(String(254), unique=True) |
| 52 | + age: Mapped[int] = mapped_column() |
| 53 | + is_active: Mapped[Optional[bool]] = mapped_column(Boolean, default=True) |
| 54 | + created_at: Mapped[Optional[datetime]] = mapped_column(DateTime, default=datetime.now(timezone.utc)) |
53 | 55 |
|
54 | | - name = Column(String(100), nullable=False) |
55 | | - email = Column(String(254), nullable=False, unique=True) |
56 | | - age = Column(Integer, nullable=False) |
57 | | - is_active = Column(Boolean, default=True) |
58 | | - created_at = Column(DateTime, default=datetime.now(timezone.utc)) |
59 | 56 |
|
60 | 57 | class DataTypeModel(Base): |
61 | 58 | """Model for testing various data types""" |
62 | 59 | __tablename__ = 'sqlalchemy_data_type_model' |
63 | 60 |
|
64 | | - id = Column(Integer, primary_key=True) |
| 61 | + id: Mapped[int] = mapped_column(primary_key=True) |
65 | 62 |
|
66 | 63 | # String fields |
67 | | - string_field = Column(String(255)) |
68 | | - text_field = Column(Text) |
| 64 | + string_field: Mapped[Optional[str]] = mapped_column(String(255)) |
| 65 | + text_field: Mapped[Optional[str]] = mapped_column(Text) |
69 | 66 |
|
70 | 67 | # Numeric fields |
71 | | - integer_field = Column(Integer) |
72 | | - small_integer_field = Column(SmallInteger) |
73 | | - big_integer_field = Column(BigInteger) |
74 | | - numeric_field = Column(Numeric(10, 2)) |
75 | | - float_field = Column(Float) |
| 68 | + integer_field: Mapped[Optional[int]] = mapped_column() |
| 69 | + small_integer_field: Mapped[Optional[int]] = mapped_column(SmallInteger) |
| 70 | + big_integer_field: Mapped[Optional[int]] = mapped_column(BigInteger) |
| 71 | + numeric_field: Mapped[Optional[Decimal]] = mapped_column(Numeric(10, 2)) |
| 72 | + float_field: Mapped[Optional[float]] = mapped_column(Float) |
76 | 73 |
|
77 | 74 | # Boolean field |
78 | | - boolean_field = Column(Boolean, default=False) |
| 75 | + boolean_field: Mapped[Optional[bool]] = mapped_column(Boolean, default=False) |
79 | 76 |
|
80 | 77 | # Date/Time fields |
81 | | - date_field = Column(Date) |
82 | | - time_field = Column(Time) |
83 | | - datetime_field = Column(DateTime) |
| 78 | + date_field: Mapped[Optional[date]] = mapped_column(Date) |
| 79 | + time_field: Mapped[Optional[time]] = mapped_column(Time) |
| 80 | + datetime_field: Mapped[Optional[datetime]] = mapped_column(DateTime) |
84 | 81 |
|
85 | 82 | # JSON field (MySQL 5.7+) |
86 | | - json_field = Column(JSON) |
| 83 | + json_field: Mapped[Optional[Any]] = mapped_column(JSON) |
| 84 | + |
87 | 85 |
|
88 | 86 | class Author(Base): |
89 | 87 | """Author model for relationship testing""" |
90 | 88 | __tablename__ = 'sqlalchemy_author' |
91 | 89 |
|
92 | | - id = Column(Integer, primary_key=True) |
93 | | - name = Column(String(100), nullable=False) |
94 | | - email = Column(String(254), nullable=False) |
95 | | - birth_date = Column(Date) |
| 90 | + id: Mapped[int] = mapped_column(primary_key=True) |
| 91 | + name: Mapped[str] = mapped_column(String(100)) |
| 92 | + email: Mapped[str] = mapped_column(String(254)) |
| 93 | + birth_date: Mapped[Optional[date]] = mapped_column(Date) |
| 94 | + |
| 95 | + books: Mapped[List[Book]] = relationship(back_populates='author', cascade='all, delete-orphan') |
96 | 96 |
|
97 | | - books = relationship('Book', back_populates='author', cascade='all, delete-orphan') |
98 | 97 |
|
99 | 98 | class Book(Base): |
100 | 99 | """Book model for relationship testing""" |
101 | 100 | __tablename__ = 'sqlalchemy_book' |
102 | 101 |
|
103 | | - id = Column(Integer, primary_key=True) |
104 | | - title = Column(String(200), nullable=False) |
105 | | - author_id = Column(Integer, ForeignKey("sqlalchemy_author.id"), nullable=False) |
106 | | - publication_date = Column(Date, nullable=False) |
107 | | - pages = Column(Integer, nullable=False) |
108 | | - price = Column(Numeric(8, 2), nullable=False) |
| 102 | + id: Mapped[int] = mapped_column(primary_key=True) |
| 103 | + title: Mapped[str] = mapped_column(String(200)) |
| 104 | + author_id: Mapped[int] = mapped_column(ForeignKey("sqlalchemy_author.id")) |
| 105 | + publication_date: Mapped[date] = mapped_column(Date) |
| 106 | + pages: Mapped[int] = mapped_column() |
| 107 | + price: Mapped[Decimal] = mapped_column(Numeric(8, 2)) |
109 | 108 |
|
110 | | - author = relationship('Author', back_populates='books') |
| 109 | + author: Mapped[Author] = relationship(back_populates='books') |
111 | 110 |
|
112 | 111 | @enable_on_engines([DatabaseEngine.MYSQL]) # MySQL Specific until PG is implemented |
113 | 112 | @enable_on_deployments([DatabaseEngineDeployment.AURORA, DatabaseEngineDeployment.RDS_MULTI_AZ_CLUSTER]) |
@@ -680,6 +679,7 @@ def test_sqlalchemy_distinct_queries(self, test_environment: TestEnvironment, se |
680 | 679 | def test_sqlalchemy_load_only_and_defer(self, test_environment: TestEnvironment, session): |
681 | 680 | """Test SQLAlchemy load_only() and defer() for query optimization""" |
682 | 681 | from sqlalchemy.orm import defer, load_only |
| 682 | + |
683 | 683 | # Ensure clean slate |
684 | 684 | session.query(TestModel).delete() |
685 | 685 | session.commit() |
@@ -747,6 +747,7 @@ def test_sqlalchemy_batch_retrieval(self, test_environment: TestEnvironment, ses |
747 | 747 | def test_sqlalchemy_conditional_expressions(self, test_environment: TestEnvironment, session): |
748 | 748 | """Test SQLAlchemy case() conditional expressions""" |
749 | 749 | from sqlalchemy import String, case |
| 750 | + |
750 | 751 | # Ensure clean slate |
751 | 752 | session.query(TestModel).delete() |
752 | 753 | session.commit() |
|
0 commit comments