Skip to content

Commit

Permalink
Fix python3.11 testcase error & Allow ForeignKeyField(on_delete=NO_AC…
Browse files Browse the repository at this point in the history
…TION) (tortoise#1410)

* ci: add python3.11

* fix: testcase error with python3.11 (tortoise#1308)

* Compare safe schema sql with github action result

* chore: add comment

* Allow ForeignKeyField(on_delete=NO_ACTION) (tortoise#1393)

* fix Codacy complaint

* Update pydantic example to use OnDelete Enum

---------

Co-authored-by: long2ice <long2ice@gmail.com>
  • Loading branch information
2 people authored and esrehmki committed Aug 17, 2023
1 parent e855dd4 commit e3ed789
Show file tree
Hide file tree
Showing 15 changed files with 204 additions and 139 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Expand Up @@ -45,7 +45,7 @@ jobs:
TORTOISE_MSSQL_DRIVER: ODBC Driver 18 for SQL Server
strategy:
matrix:
python-version: ["3.8", "3.9", "3.10"]
python-version: ["3.8", "3.9", "3.10", "3.11"]
steps:
- uses: actions/cache@v3
with:
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.rst
Expand Up @@ -10,9 +10,13 @@ Changelog
====
0.19.4
------
Added
^^^^^
- Allow ForeignKeyField(on_delete=NO_ACTION) (#1393)
Fixed
^^^^^
- Fix foreign key constraint not generated on MSSQL Server. (#1400)
- Fix testcase error with python3.11 (#1308)

0.19.3
------
Expand Down
2 changes: 1 addition & 1 deletion examples/pydantic/basic.py
Expand Up @@ -39,7 +39,7 @@ class Address(Model):
created_at = fields.DatetimeField(auto_now_add=True)

event: fields.OneToOneRelation[Event] = fields.OneToOneField(
"models.Event", on_delete=fields.CASCADE, related_name="address", pk=True
"models.Event", on_delete=fields.OnDelete.CASCADE, related_name="address", pk=True
)

class Meta:
Expand Down
2 changes: 1 addition & 1 deletion examples/relations.py
Expand Up @@ -39,7 +39,7 @@ class Address(Model):
street = fields.CharField(max_length=128)

event: fields.OneToOneRelation[Event] = fields.OneToOneField(
"models.Event", on_delete=fields.CASCADE, related_name="address", pk=True
"models.Event", on_delete=fields.OnDelete.CASCADE, related_name="address", pk=True
)

def __str__(self):
Expand Down
2 changes: 1 addition & 1 deletion examples/relations_with_unique.py
Expand Up @@ -30,7 +30,7 @@ class Principal(Model):
id = fields.IntField(pk=True)
name = fields.TextField()
school: fields.OneToOneRelation[School] = fields.OneToOneField(
"models.School", on_delete=fields.CASCADE, related_name="principal", to_field="id"
"models.School", on_delete=fields.OnDelete.CASCADE, related_name="principal", to_field="id"
)


Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Expand Up @@ -22,6 +22,7 @@ classifiers = [
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: Implementation :: CPython",
"Programming Language :: Python :: Implementation :: PyPy",
"Programming Language :: PL/SQL",
Expand Down
2 changes: 1 addition & 1 deletion tests/schema/models_fk_2.py
Expand Up @@ -8,5 +8,5 @@

class One(Model):
tournament: fields.ForeignKeyRelation[Two] = fields.ForeignKeyField(
"models.Two", on_delete="WABOOM"
"models.Two", on_delete="WABOOM" # type:ignore
)
2 changes: 1 addition & 1 deletion tests/schema/models_o2o_2.py
Expand Up @@ -8,5 +8,5 @@

class One(Model):
tournament: fields.OneToOneRelation[Two] = fields.OneToOneField(
"models.Two", on_delete="WABOOM"
"models.Two", on_delete="WABOOM" # type:ignore
)
178 changes: 91 additions & 87 deletions tests/schema/test_generate_schema.py
Expand Up @@ -9,6 +9,86 @@


class TestGenerateSchema(test.SimpleTestCase):
safe_schema_sql = """
CREATE TABLE IF NOT EXISTS "company" (
"id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
"name" TEXT NOT NULL,
"uuid" CHAR(36) NOT NULL UNIQUE
);
CREATE TABLE IF NOT EXISTS "defaultpk" (
"id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
"val" INT NOT NULL
);
CREATE TABLE IF NOT EXISTS "employee" (
"id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
"name" TEXT NOT NULL,
"company_id" CHAR(36) NOT NULL REFERENCES "company" ("uuid") ON DELETE CASCADE
);
CREATE TABLE IF NOT EXISTS "inheritedmodel" (
"id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
"zero" INT NOT NULL,
"one" VARCHAR(40),
"new_field" VARCHAR(100) NOT NULL,
"two" VARCHAR(40) NOT NULL,
"name" TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS "sometable" (
"sometable_id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
"some_chars_table" VARCHAR(255) NOT NULL,
"fk_sometable" INT REFERENCES "sometable" ("sometable_id") ON DELETE CASCADE
);
CREATE INDEX IF NOT EXISTS "idx_sometable_some_ch_3d69eb" ON "sometable" ("some_chars_table");
CREATE TABLE IF NOT EXISTS "team" (
"name" VARCHAR(50) NOT NULL PRIMARY KEY /* The TEAM name (and PK) */,
"key" INT NOT NULL,
"manager_id" VARCHAR(50) REFERENCES "team" ("name") ON DELETE CASCADE
) /* The TEAMS! */;
CREATE INDEX IF NOT EXISTS "idx_team_manager_676134" ON "team" ("manager_id", "key");
CREATE INDEX IF NOT EXISTS "idx_team_manager_ef8f69" ON "team" ("manager_id", "name");
CREATE TABLE IF NOT EXISTS "teamaddress" (
"city" VARCHAR(50) NOT NULL /* City */,
"country" VARCHAR(50) NOT NULL /* Country */,
"street" VARCHAR(128) NOT NULL /* Street Address */,
"team_id" VARCHAR(50) NOT NULL PRIMARY KEY REFERENCES "team" ("name") ON DELETE CASCADE
) /* The Team's address */;
CREATE TABLE IF NOT EXISTS "tournament" (
"tid" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
"name" VARCHAR(100) NOT NULL /* Tournament name */,
"created" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP /* Created *\\/'`\\/* datetime */
) /* What Tournaments *\\/'`\\/* we have */;
CREATE INDEX IF NOT EXISTS "idx_tournament_name_6fe200" ON "tournament" ("name");
CREATE TABLE IF NOT EXISTS "event" (
"id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL /* Event ID */,
"name" TEXT NOT NULL,
"modified" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
"prize" VARCHAR(40),
"token" VARCHAR(100) NOT NULL UNIQUE /* Unique token */,
"key" VARCHAR(100) NOT NULL,
"tournament_id" SMALLINT NOT NULL REFERENCES "tournament" ("tid") ON DELETE CASCADE /* FK to tournament */,
CONSTRAINT "uid_event_name_c6f89f" UNIQUE ("name", "prize"),
CONSTRAINT "uid_event_tournam_a5b730" UNIQUE ("tournament_id", "key")
) /* This table contains a list of all the events */;
CREATE TABLE IF NOT EXISTS "venueinformation" (
"id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
"name" VARCHAR(128) NOT NULL,
"capacity" INT NOT NULL /* No. of seats */,
"rent" REAL NOT NULL,
"team_id" VARCHAR(50) UNIQUE REFERENCES "team" ("name") ON DELETE SET NULL
);
CREATE TABLE IF NOT EXISTS "sometable_self" (
"backward_sts" INT NOT NULL REFERENCES "sometable" ("sometable_id") ON DELETE CASCADE,
"sts_forward" INT NOT NULL REFERENCES "sometable" ("sometable_id") ON DELETE CASCADE
);
CREATE TABLE IF NOT EXISTS "team_team" (
"team_rel_id" VARCHAR(50) NOT NULL REFERENCES "team" ("name") ON DELETE CASCADE,
"team_id" VARCHAR(50) NOT NULL REFERENCES "team" ("name") ON DELETE CASCADE
);
CREATE TABLE IF NOT EXISTS "teamevents" (
"event_id" BIGINT NOT NULL REFERENCES "event" ("id") ON DELETE SET NULL,
"team_id" VARCHAR(50) NOT NULL REFERENCES "team" ("name") ON DELETE SET NULL
) /* How participants relate */;
""".strip()

async def asyncSetUp(self):
await super().asyncSetUp()
try:
Expand Down Expand Up @@ -99,7 +179,8 @@ async def test_fk_bad_model_name(self):

async def test_fk_bad_on_delete(self):
with self.assertRaisesRegex(
ConfigurationError, "on_delete can only be CASCADE, RESTRICT or SET_NULL"
ConfigurationError,
"on_delete can only be CASCADE, RESTRICT, SET_NULL, SET_DEFAULT or NO_ACTION",
):
await self.init_for("tests.schema.models_fk_2")

Expand All @@ -111,7 +192,8 @@ async def test_fk_bad_null(self):

async def test_o2o_bad_on_delete(self):
with self.assertRaisesRegex(
ConfigurationError, "on_delete can only be CASCADE, RESTRICT or SET_NULL"
ConfigurationError,
"on_delete can only be CASCADE, RESTRICT, SET_NULL, SET_DEFAULT or NO_ACTION",
):
await self.init_for("tests.schema.models_o2o_2")

Expand Down Expand Up @@ -265,88 +347,7 @@ async def test_schema_safe(self):
self.maxDiff = None
await self.init_for("tests.schema.models_schema_create")
sql = get_schema_sql(connections.get("default"), safe=True)
self.assertEqual(
sql.strip(),
"""
CREATE TABLE IF NOT EXISTS "company" (
"id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
"name" TEXT NOT NULL,
"uuid" CHAR(36) NOT NULL UNIQUE
);
CREATE TABLE IF NOT EXISTS "defaultpk" (
"id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
"val" INT NOT NULL
);
CREATE TABLE IF NOT EXISTS "employee" (
"id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
"name" TEXT NOT NULL,
"company_id" CHAR(36) NOT NULL REFERENCES "company" ("uuid") ON DELETE CASCADE
);
CREATE TABLE IF NOT EXISTS "inheritedmodel" (
"id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
"zero" INT NOT NULL,
"one" VARCHAR(40),
"new_field" VARCHAR(100) NOT NULL,
"two" VARCHAR(40) NOT NULL,
"name" TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS "sometable" (
"sometable_id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
"some_chars_table" VARCHAR(255) NOT NULL,
"fk_sometable" INT REFERENCES "sometable" ("sometable_id") ON DELETE CASCADE
);
CREATE INDEX IF NOT EXISTS "idx_sometable_some_ch_3d69eb" ON "sometable" ("some_chars_table");
CREATE TABLE IF NOT EXISTS "team" (
"name" VARCHAR(50) NOT NULL PRIMARY KEY /* The TEAM name (and PK) */,
"key" INT NOT NULL,
"manager_id" VARCHAR(50) REFERENCES "team" ("name") ON DELETE CASCADE
) /* The TEAMS! */;
CREATE INDEX IF NOT EXISTS "idx_team_manager_676134" ON "team" ("manager_id", "key");
CREATE INDEX IF NOT EXISTS "idx_team_manager_ef8f69" ON "team" ("manager_id", "name");
CREATE TABLE IF NOT EXISTS "teamaddress" (
"city" VARCHAR(50) NOT NULL /* City */,
"country" VARCHAR(50) NOT NULL /* Country */,
"street" VARCHAR(128) NOT NULL /* Street Address */,
"team_id" VARCHAR(50) NOT NULL PRIMARY KEY REFERENCES "team" ("name") ON DELETE CASCADE
) /* The Team's address */;
CREATE TABLE IF NOT EXISTS "tournament" (
"tid" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
"name" VARCHAR(100) NOT NULL /* Tournament name */,
"created" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP /* Created *\\/'`\\/* datetime */
) /* What Tournaments *\\/'`\\/* we have */;
CREATE INDEX IF NOT EXISTS "idx_tournament_name_6fe200" ON "tournament" ("name");
CREATE TABLE IF NOT EXISTS "event" (
"id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL /* Event ID */,
"name" TEXT NOT NULL,
"modified" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
"prize" VARCHAR(40),
"token" VARCHAR(100) NOT NULL UNIQUE /* Unique token */,
"key" VARCHAR(100) NOT NULL,
"tournament_id" SMALLINT NOT NULL REFERENCES "tournament" ("tid") ON DELETE CASCADE /* FK to tournament */,
CONSTRAINT "uid_event_name_c6f89f" UNIQUE ("name", "prize"),
CONSTRAINT "uid_event_tournam_a5b730" UNIQUE ("tournament_id", "key")
) /* This table contains a list of all the events */;
CREATE TABLE IF NOT EXISTS "venueinformation" (
"id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
"name" VARCHAR(128) NOT NULL,
"capacity" INT NOT NULL /* No. of seats */,
"rent" REAL NOT NULL,
"team_id" VARCHAR(50) UNIQUE REFERENCES "team" ("name") ON DELETE SET NULL
);
CREATE TABLE IF NOT EXISTS "sometable_self" (
"backward_sts" INT NOT NULL REFERENCES "sometable" ("sometable_id") ON DELETE CASCADE,
"sts_forward" INT NOT NULL REFERENCES "sometable" ("sometable_id") ON DELETE CASCADE
);
CREATE TABLE IF NOT EXISTS "team_team" (
"team_rel_id" VARCHAR(50) NOT NULL REFERENCES "team" ("name") ON DELETE CASCADE,
"team_id" VARCHAR(50) NOT NULL REFERENCES "team" ("name") ON DELETE CASCADE
);
CREATE TABLE IF NOT EXISTS "teamevents" (
"event_id" BIGINT NOT NULL REFERENCES "event" ("id") ON DELETE SET NULL,
"team_id" VARCHAR(50) NOT NULL REFERENCES "team" ("name") ON DELETE SET NULL
) /* How participants relate */;
""".strip(),
)
self.assertEqual(sql.strip(), self.safe_schema_sql)

async def test_m2m_no_auto_create(self):
self.maxDiff = None
Expand Down Expand Up @@ -597,10 +598,13 @@ async def test_schema(self):
async def test_schema_safe(self):
self.maxDiff = None
await self.init_for("tests.schema.models_schema_create")
sql = get_schema_sql(connections.get("default"), safe=True)

sql = get_schema_sql(connections.get("default"), safe=True).strip()
if sql == self.safe_schema_sql:
# Sometimes github action get different result from local machine(Ubuntu20)
self.assertEqual(sql, self.safe_schema_sql)
return
self.assertEqual(
sql.strip(),
sql,
"""
CREATE TABLE IF NOT EXISTS `company` (
`id` INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
Expand Down
29 changes: 20 additions & 9 deletions tests/testmodels.py
Expand Up @@ -15,6 +15,7 @@

from tortoise import fields
from tortoise.exceptions import NoValuesFetched, ValidationError
from tortoise.fields import NO_ACTION
from tortoise.manager import Manager
from tortoise.models import Model
from tortoise.queryset import QuerySet
Expand Down Expand Up @@ -121,7 +122,7 @@ class Tree(Model):
"models.Node", related_name="parent_trees"
)
child: fields.ForeignKeyRelation[Node] = fields.ForeignKeyField(
"models.Node", related_name="children_trees"
"models.Node", related_name="children_trees", on_delete=NO_ACTION
)


Expand Down Expand Up @@ -377,7 +378,7 @@ class UUIDFkRelatedNullModel(Model):
"models.UUIDPkModel", related_name=False, null=True
)
parent: fields.OneToOneNullableRelation[UUIDPkModel] = fields.OneToOneField(
"models.UUIDPkModel", related_name=False, null=True
"models.UUIDPkModel", related_name=False, null=True, on_delete=NO_ACTION
)


Expand Down Expand Up @@ -483,12 +484,12 @@ class Employee(Model):
name = fields.CharField(max_length=50)

manager: fields.ForeignKeyNullableRelation["Employee"] = fields.ForeignKeyField(
"models.Employee", related_name="team_members", null=True
"models.Employee", related_name="team_members", null=True, on_delete=NO_ACTION
)
team_members: fields.ReverseRelation["Employee"]

talks_to: fields.ManyToManyRelation["Employee"] = fields.ManyToManyField(
"models.Employee", related_name="gets_talked_to", on_delete=fields.NO_ACTION
"models.Employee", related_name="gets_talked_to", on_delete=NO_ACTION
)
gets_talked_to: fields.ManyToManyRelation["Employee"]

Expand Down Expand Up @@ -578,12 +579,20 @@ class StraightFields(Model):
nullable = fields.CharField(max_length=50, null=True)

fk: fields.ForeignKeyNullableRelation["StraightFields"] = fields.ForeignKeyField(
"models.StraightFields", related_name="fkrev", null=True, description="Tree!"
"models.StraightFields",
related_name="fkrev",
null=True,
description="Tree!",
on_delete=NO_ACTION,
)
fkrev: fields.ReverseRelation["StraightFields"]

o2o: fields.OneToOneNullableRelation["StraightFields"] = fields.OneToOneField(
"models.StraightFields", related_name="o2o_rev", null=True, description="Line"
"models.StraightFields",
related_name="o2o_rev",
null=True,
description="Line",
on_delete=NO_ACTION,
)
o2o_rev: fields.Field

Expand Down Expand Up @@ -620,6 +629,7 @@ class SourceFields(Model):
null=True,
source_field="fk_sometable",
description="Tree!",
on_delete=NO_ACTION,
)
fkrev: fields.ReverseRelation["SourceFields"]

Expand All @@ -629,6 +639,7 @@ class SourceFields(Model):
null=True,
source_field="o2o_sometable",
description="Line",
on_delete=NO_ACTION,
)
o2o_rev: fields.Field

Expand Down Expand Up @@ -669,10 +680,10 @@ class EnumFields(Model):
class DoubleFK(Model):
name = fields.CharField(max_length=50)
left: fields.ForeignKeyNullableRelation["DoubleFK"] = fields.ForeignKeyField(
"models.DoubleFK", null=True, related_name="left_rel"
"models.DoubleFK", null=True, related_name="left_rel", on_delete=NO_ACTION
)
right: fields.ForeignKeyNullableRelation["DoubleFK"] = fields.ForeignKeyField(
"models.DoubleFK", null=True, related_name="right_rel"
"models.DoubleFK", null=True, related_name="right_rel", on_delete=NO_ACTION
)


Expand Down Expand Up @@ -850,7 +861,7 @@ class Pair(Model):
"models.Single", related_name="lefts", null=True
)
right: fields.ForeignKeyNullableRelation[Single] = fields.ForeignKeyField(
"models.Single", related_name="rights", null=True
"models.Single", related_name="rights", null=True, on_delete=NO_ACTION
)


Expand Down

0 comments on commit e3ed789

Please sign in to comment.