Skip to content

Commit 2bd840a

Browse files
committed
Merge branch 'develop' of github.com:tortoise/tortoise-orm into starlette-v1
2 parents a34d6b4 + bc4f971 commit 2bd840a

8 files changed

Lines changed: 64 additions & 4 deletions

File tree

CHANGELOG.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,10 @@ Added
2121
Fixed
2222
^^^^^
2323
- ``MigrationRecorder`` now uses parameterized queries; fixes MariaDB/MySQL rejecting ISO-8601 ``applied_at`` values. (#2132)
24+
- ``db_default`` on ``ForeignKeyField``/``OneToOneField`` now propagates to the underlying ``<fk>_id`` column, so ``CREATE TABLE`` emits the ``DEFAULT`` clause for FK columns. (#2199)
25+
- ``MigrationRecorder`` no longer emits tortoise's own ``pk`` field ``DeprecationWarning`` when applying migrations; it now builds its bookkeeping model with ``primary_key=True``. (#2203)
2426
- ``QuerySet.count()`` now matches the limited query result for the LIMIT/OFFSET edge cases: it returns ``0`` (instead of a negative number) when ``offset()`` exceeds the total row count, and ``0`` (instead of the total) for ``limit(0)``. (#2208)
27+
- Field declarations on models now resolve to their concrete type (e.g. ``CharField[str]``) in Pyright/Pylance instead of ``Field[Unknown]``; the ``Field.__new__`` type-check stub now returns ``Self``. (#2216)
2528

2629
1.1.7
2730
-----

tests/backends/test_db_url.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
_postgres_scheme_engines = {
99
"postgres": "tortoise.backends.asyncpg",
10+
"postgresql": "tortoise.backends.asyncpg",
1011
"asyncpg": "tortoise.backends.asyncpg",
1112
"psycopg": "tortoise.backends.psycopg",
1213
}

tests/migrations/test_recorder.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import warnings
2+
13
import pytest
24

35
from tortoise.backends.base.client import Capabilities
@@ -126,6 +128,22 @@ async def test_recorder_sqlite_placeholders() -> None:
126128
assert '"name" = ?' in delete_query
127129

128130

131+
def test_recorder_model_emits_no_field_deprecation_warnings() -> None:
132+
"""Regression: the migration recorder must not trip tortoise's own
133+
``pk``/``index`` field deprecation warnings. It builds its bookkeeping
134+
model internally, so downstream projects can't silence them.
135+
"""
136+
with warnings.catch_warnings(record=True) as caught:
137+
# Reset filters so the repo-wide ``ignore:`pk` deprecation`` filter in
138+
# pyproject.toml doesn't mask a regression here.
139+
warnings.simplefilter("always")
140+
MigrationRecorder(FakeConnection("sqlite"))
141+
142+
messages = [str(w.message) for w in caught if issubclass(w.category, DeprecationWarning)]
143+
assert not any("`pk` is deprecated" in m for m in messages), messages
144+
assert not any("`index` is deprecated" in m for m in messages), messages
145+
146+
129147
@pytest.mark.asyncio
130148
async def test_recorder_oracle_placeholders() -> None:
131149
connection = FakeConnection("oracle")

tests/migrations/test_schema_editor_sql.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,3 +249,36 @@ class Meta:
249249
sql = client.executed[0]
250250
assert 'CREATE TABLE "widget"' in sql
251251
assert "DEFAULT 'active'" in sql
252+
253+
254+
@pytest.mark.asyncio
255+
async def test_create_model_includes_db_default_on_fk() -> None:
256+
"""CreateModel should include DEFAULT clause for FK columns with db_default."""
257+
258+
class Dc(Model):
259+
id = fields.IntField(primary_key=True)
260+
261+
class Meta:
262+
table = "dc"
263+
app = "models"
264+
265+
class App(Model):
266+
id = fields.IntField(primary_key=True)
267+
dc: fields.ForeignKeyRelation[Dc] = fields.ForeignKeyField("models.Dc", db_default=2)
268+
269+
class Meta:
270+
table = "app"
271+
app = "models"
272+
273+
init_apps(Dc, App)
274+
275+
client = FakeClient("sql")
276+
editor = TestSchemaEditor(client)
277+
278+
await editor.create_model(App)
279+
280+
assert len(client.executed) == 1
281+
sql = client.executed[0]
282+
assert 'CREATE TABLE "app"' in sql
283+
assert '"dc_id"' in sql
284+
assert "DEFAULT 2" in sql

tortoise/apps.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ def init_fk_o2o_field(model: type[Model], field: str, is_o2o: bool = False) -> N
192192
key_field = f"{field}_id"
193193
key_fk_object.reference = fk_object
194194
key_fk_object.source_field = fk_object.source_field or key_field
195-
for attr in ("index", "default", "null", "generated", "description"):
195+
for attr in ("index", "default", "null", "generated", "description", "db_default"):
196196
setattr(key_fk_object, attr, getattr(fk_object, attr))
197197
if is_o2o:
198198
key_fk_object.pk = fk_object.pk

tortoise/backends/base/config_generator.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from tortoise.exceptions import ConfigurationError
1010

1111
urlparse.uses_netloc.append("postgres")
12+
urlparse.uses_netloc.append("postgresql")
1213
urlparse.uses_netloc.append("asyncpg")
1314
urlparse.uses_netloc.append("psycopg")
1415
urlparse.uses_netloc.append("sqlite")
@@ -127,6 +128,8 @@
127128
}
128129
# Create an alias for backwards compatibility
129130
DB_LOOKUP["postgres"] = DB_LOOKUP["asyncpg"]
131+
# "postgresql" is the scheme accepted by libpq and pydantic's PostgresDsn
132+
DB_LOOKUP["postgresql"] = DB_LOOKUP["asyncpg"]
130133

131134

132135
def _quote_url_userinfo(db_url: str) -> str:
@@ -211,7 +214,7 @@ def expand_db_url(db_url: str, testing: bool = False) -> dict:
211214
# asyncpg accepts None for password, but aiomysql not
212215
params[vmap["password"]] = (
213216
None
214-
if (not url.password and db_backend in {"postgres", "asyncpg", "psycopg"})
217+
if (not url.password and db_backend in {"postgres", "postgresql", "asyncpg", "psycopg"})
215218
else urlparse.unquote(url.password or "")
216219
)
217220

tortoise/fields/base.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@
1919

2020
if sys.version_info >= (3, 11):
2121
from enum import StrEnum
22+
from typing import Self
2223
else: # pragma: no cover
24+
from typing_extensions import Self
2325

2426
class StrEnum(str, Enum):
2527
__str__ = str.__str__
@@ -202,7 +204,7 @@ def function_cast(self, term: Term) -> Term:
202204
# These methods are just to make IDE/Linters happy:
203205
if TYPE_CHECKING:
204206

205-
def __new__(cls, *args: Any, **kwargs: Any) -> Field[VALUE]:
207+
def __new__(cls, *args: Any, **kwargs: Any) -> Self:
206208
return super().__new__(cls)
207209

208210
@overload

tortoise/migrations/recorder.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ def _placeholder(self, pos: int) -> str:
3535

3636
def _make_model(self, table_name: str) -> type[Model]:
3737
class MigrationRecord(Model):
38-
id = fields.IntField(pk=True)
38+
id = fields.IntField(primary_key=True)
3939
app = fields.CharField(max_length=255)
4040
name = fields.CharField(max_length=255)
4141
applied_at = fields.DatetimeField()

0 commit comments

Comments
 (0)