diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 10801ab3f..f08dec0ff 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -15,6 +15,7 @@ Changed ^^^^^^^ - Skip database selection if the router is not configured to improve performance (#1915) - `.values()`, `.values_list()` and `.only()` cannot be used together (#1923) +- ForeignKeyField: The `on_delete` parameter is now mandatory. Users must now explicitly specify the desired deletion behavior (e.g., `on_delete=fields.RESTRICT`, `on_delete=fields.CASCADE`, etc.) (#1801) Added ^^^^^ diff --git a/docs/contrib/pydantic.rst b/docs/contrib/pydantic.rst index 919fd0b0d..11734750b 100644 --- a/docs/contrib/pydantic.rst +++ b/docs/contrib/pydantic.rst @@ -293,7 +293,7 @@ We define our models with a relationship: created_at = fields.DatetimeField(auto_now_add=True) tournament = fields.ForeignKeyField( - "models.Tournament", related_name="events", description="The Tournament this happens in" + "models.Tournament", related_name="events", description="The Tournament this happens in", on_delete=fields.RESTRICT ) Next we create our `Pydantic Model `__ using ``pydantic_model_creator``: @@ -573,7 +573,7 @@ Let's add some methods that calculate data, and tell the creators to use them: created_at = fields.DatetimeField(auto_now_add=True) tournament = fields.ForeignKeyField( - "models.Tournament", related_name="events", description="The Tournament this happens in" + "models.Tournament", related_name="events", description="The Tournament this happens in", on_delete=fields.RESTRICT ) class Meta: diff --git a/docs/getting_started.rst b/docs/getting_started.rst index d5cf16a9a..982023bca 100644 --- a/docs/getting_started.rst +++ b/docs/getting_started.rst @@ -68,7 +68,7 @@ Define the models by inheriting from ``tortoise.models.Model``. name = fields.CharField(max_length=255) # References to other models are defined in format # "{app_name}.{model_name}" - where {app_name} is defined in the tortoise config - tournament = fields.ForeignKeyField('models.Tournament', related_name='events') + tournament = fields.ForeignKeyField('models.Tournament', related_name='events', on_delete=fields.RESTRICT) participants = fields.ManyToManyField('models.Team', related_name='events', through='event_team') diff --git a/docs/models.rst b/docs/models.rst index 8c1bd48ba..8a0c90b77 100644 --- a/docs/models.rst +++ b/docs/models.rst @@ -31,7 +31,7 @@ With that start describing the models class Event(Model): id = fields.IntField(primary_key=True) name = fields.TextField() - tournament = fields.ForeignKeyField('models.Tournament', related_name='events') + tournament = fields.ForeignKeyField('models.Tournament', related_name='events', on_delete=fields.RESTRICT) participants = fields.ManyToManyField('models.Team', related_name='events', through='event_team') modified = fields.DatetimeField(auto_now=True) prize = fields.DecimalField(max_digits=10, decimal_places=2, null=True) @@ -249,14 +249,14 @@ The ``Meta`` class .. code-block:: python3 - tournament = fields.ForeignKeyField('models.Tournament', related_name='events') + tournament = fields.ForeignKeyField('models.Tournament', related_name='events', on_delete=fields.RESTRICT) participants = fields.ManyToManyField('models.Team', related_name='events') modified = fields.DatetimeField(auto_now=True) prize = fields.DecimalField(max_digits=10, decimal_places=2, null=True) In event model we got some more fields, that could be interesting for us. -``fields.ForeignKeyField('models.Tournament', related_name='events')`` +``fields.ForeignKeyField('models.Tournament', related_name='events', on_delete=fields.RESTRICT)`` Here we create foreign key reference to tournament. We create it by referring to model by it's literal, consisting of app name and model name. ``models`` is default app name, but you can change it in ``class Meta`` with ``app = 'other'``. ``related_name`` Is keyword argument, that defines field for related query on referenced models, so with that you could fetch all tournaments's events with like this: @@ -355,6 +355,93 @@ To get the Reverse-FK, e.g. an `event.tournament` we currently only support the await event.fetch_related('tournament') tournament = event.tournament +.. _on-delete: + +ForeignKeyField: The on_delete Parameter +---------------------------------------- + +The ``on_delete`` parameter is a **mandatory** argument for the :class:`~tortoise.fields.ForeignKeyField`. It specifies the behavior when a referenced object is deleted. Previously, ``on_delete`` defaulted to ``CASCADE``, but this behavior has been removed to prevent accidental data loss. You *must* now explicitly specify the desired behavior. + +Here are the possible values for ``on_delete``: + +* **``fields.CASCADE``**: When the referenced object is deleted, also delete the objects that have references to it. **WARNING:** This can lead to data loss if not used carefully. Consider the implications before using ``CASCADE``. + + .. code-block:: python3 + + from tortoise import fields + from tortoise.models import Model + + class Parent(Model): + id = fields.IntField(primary_key=True) + + class Child(Model): + id = fields.IntField(primary_key=True) + parent = fields.ForeignKeyField("models.Parent", on_delete=fields.CASCADE) + # When a Parent object is deleted, all related Child objects will also be deleted. + +* **``fields.RESTRICT``**: Prevent deletion of the referenced object if it is referenced by any other objects. This is the **recommended** option for most cases, as it prevents accidental data loss. Attempting to delete a referenced object will raise a ``tortoise.exceptions.IntegrityError``. + + .. code-block:: python3 + + from tortoise import fields + from tortoise.models import Model + + class Parent(Model): + id = fields.IntField(primary_key=True) + + class Child(Model): + id = fields.IntField(primary_key=True) + parent = fields.ForeignKeyField("models.Parent", on_delete=fields.RESTRICT) + # Attempting to delete a Parent object that has related Child objects will raise an error. + +* **``fields.SET_NULL``**: When the referenced object is deleted, set the foreign key field to ``NULL``. This option is only valid if the ``ForeignKeyField`` has ``null=True``. + + .. code-block:: python3 + + from tortoise import fields + from tortoise.models import Model + + class Parent(Model): + id = fields.IntField(primary_key=True) + + class Child(Model): + id = fields.IntField(primary_key=True) + parent = fields.ForeignKeyField("models.Parent", on_delete=fields.SET_NULL, null=True) + # When a Parent object is deleted, the 'parent' field in related Child objects will be set to NULL. + +* **``fields.SET_DEFAULT``**: When the referenced object is deleted, set the foreign key field to its default value. This option is only valid if the ``ForeignKeyField`` has a ``default`` value specified. + + .. code-block:: python3 + + from tortoise import fields + from tortoise.models import Model + + class Parent(Model): + id = fields.IntField(primary_key=True) + + class Child(Model): + id = fields.IntField(primary_key=True) + parent = fields.ForeignKeyField("models.Parent", on_delete=fields.SET_DEFAULT, default=1) + # When a Parent object is deleted, the 'parent' field in related Child objects will be set to its default value (1 in this case). + +* **``fields.NO_ACTION``**: Take no action on the database when the referenced object is deleted. This means that the database will enforce referential integrity, and you will get an error if you try to delete a referenced object. This option is rarely used and its behavior may depend on the specific database backend. It is generally recommended to use ``RESTRICT`` instead. + + .. code-block:: python3 + + from tortoise import fields + from tortoise.models import Model + + class Parent(Model): + id = fields.IntField(primary_key=True) + + class Child(Model): + id = fields.IntField(primary_key=True) + parent = fields.ForeignKeyField("models.Parent", on_delete=fields.NO_ACTION) + # Attempting to delete a Parent object that has related Child objects will raise a database error. + +**Choosing the Right ``on_delete`` Value:** + +The best ``on_delete`` value depends on your specific application and the relationship between your models. In most cases, ``RESTRICT`` is the safest and most appropriate option. Consider the implications of each option carefully before making a decision. Always prioritize data integrity and avoid accidental data loss. ``ManyToManyField`` ------------------- @@ -426,7 +513,7 @@ all models including fields for the relations between models. id = fields.IntField(primary_key=True) name = fields.CharField(max_length=255) tournament: fields.ForeignKeyRelation[Tournament] = fields.ForeignKeyField( - "models.Tournament", related_name="events" + "models.Tournament", related_name="events", on_delete=fields.RESTRICT ) participants: fields.ManyToManyRelation["Team"] = fields.ManyToManyField( "models.Team", related_name="events", through="event_team" diff --git a/examples/complex_filtering.py b/examples/complex_filtering.py index d9d1f2207..2b5efda38 100644 --- a/examples/complex_filtering.py +++ b/examples/complex_filtering.py @@ -23,7 +23,7 @@ class Event(Model): id = fields.IntField(primary_key=True) name = fields.TextField() tournament: fields.ForeignKeyRelation[Tournament] = fields.ForeignKeyField( - "models.Tournament", related_name="events" + "models.Tournament", related_name="events", on_delete=fields.RESTRICT ) participants: fields.ManyToManyRelation["Team"] = fields.ManyToManyField( "models.Team", related_name="events", through="event_team" diff --git a/examples/complex_prefetching.py b/examples/complex_prefetching.py index c458fecb4..e0e0f3675 100644 --- a/examples/complex_prefetching.py +++ b/examples/complex_prefetching.py @@ -17,7 +17,7 @@ class Event(Model): id = fields.IntField(primary_key=True) name = fields.TextField() tournament: fields.ForeignKeyRelation[Tournament] = fields.ForeignKeyField( - "models.Tournament", related_name="events" + "models.Tournament", related_name="events", on_delete=fields.RESTRICT ) participants: fields.ManyToManyRelation["Team"] = fields.ManyToManyField( "models.Team", related_name="events", through="event_team" diff --git a/examples/functions.py b/examples/functions.py index 210aaac64..6fe9b3259 100644 --- a/examples/functions.py +++ b/examples/functions.py @@ -19,7 +19,7 @@ class Event(Model): id = fields.IntField(primary_key=True) name = fields.TextField() tournament: fields.ForeignKeyRelation[Tournament] = fields.ForeignKeyField( - "models.Tournament", related_name="events" + "models.Tournament", related_name="events", on_delete=fields.RESTRICT ) participants: fields.ManyToManyRelation["Team"] = fields.ManyToManyField( "models.Team", related_name="events", through="event_team" diff --git a/examples/global_table_name_generator.py b/examples/global_table_name_generator.py index 0bf43eb0c..14f90ef35 100644 --- a/examples/global_table_name_generator.py +++ b/examples/global_table_name_generator.py @@ -26,7 +26,7 @@ class BlogPost(Model): id = fields.IntField(primary_key=True) title = fields.TextField() author: fields.ForeignKeyRelation[UserProfile] = fields.ForeignKeyField( - "models.UserProfile", related_name="posts" + "models.UserProfile", related_name="posts", on_delete=fields.RESTRICT ) class Meta: diff --git a/examples/group_by.py b/examples/group_by.py index 7ca9b10fe..61e3e835f 100644 --- a/examples/group_by.py +++ b/examples/group_by.py @@ -9,7 +9,7 @@ class Author(Model): class Book(Model): name = fields.CharField(max_length=255) author: fields.ForeignKeyRelation[Author] = fields.ForeignKeyField( - "models.Author", related_name="books" + "models.Author", related_name="books", on_delete=fields.RESTRICT ) rating = fields.FloatField() diff --git a/examples/pydantic/basic.py b/examples/pydantic/basic.py index b7075c8f3..5b7ce965b 100644 --- a/examples/pydantic/basic.py +++ b/examples/pydantic/basic.py @@ -23,7 +23,7 @@ class Event(Model): name = fields.TextField() created_at = fields.DatetimeField(auto_now_add=True) tournament: fields.ForeignKeyNullableRelation[Tournament] = fields.ForeignKeyField( - "models.Tournament", related_name="events", null=True + "models.Tournament", related_name="events", null=True, on_delete=fields.RESTRICT ) participants: fields.ManyToManyRelation["Team"] = fields.ManyToManyField( "models.Team", related_name="events", through="event_team" diff --git a/examples/pydantic/early_init.py b/examples/pydantic/early_init.py index 5c791790d..aabac5000 100644 --- a/examples/pydantic/early_init.py +++ b/examples/pydantic/early_init.py @@ -23,7 +23,7 @@ class Event(Model): name = fields.TextField() created_at = fields.DatetimeField(auto_now_add=True) tournament: fields.ForeignKeyNullableRelation[Tournament] = fields.ForeignKeyField( - "models.Tournament", related_name="events", null=True + "models.Tournament", related_name="events", null=True, on_delete=fields.RESTRICT ) class Meta: diff --git a/examples/pydantic/recursive.py b/examples/pydantic/recursive.py index a748a216f..c8aaca3dc 100644 --- a/examples/pydantic/recursive.py +++ b/examples/pydantic/recursive.py @@ -12,7 +12,7 @@ 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=fields.RESTRICT ) team_members: fields.ReverseRelation["Employee"] diff --git a/examples/pydantic/tutorial_3.py b/examples/pydantic/tutorial_3.py index 6c8ba40d4..e152a7863 100644 --- a/examples/pydantic/tutorial_3.py +++ b/examples/pydantic/tutorial_3.py @@ -32,7 +32,10 @@ class Event(Model): created_at = fields.DatetimeField(auto_now_add=True) tournament: fields.ForeignKeyRelation[Tournament] = fields.ForeignKeyField( - "models.Tournament", related_name="events", description="The Tournament this happens in" + "models.Tournament", + related_name="events", + description="The Tournament this happens in", + on_delete=fields.RESTRICT, ) diff --git a/examples/pydantic/tutorial_4.py b/examples/pydantic/tutorial_4.py index ecaa9b5ee..530b4b0d9 100644 --- a/examples/pydantic/tutorial_4.py +++ b/examples/pydantic/tutorial_4.py @@ -67,7 +67,10 @@ class Event(Model): created_at = fields.DatetimeField(auto_now_add=True) tournament: fields.ForeignKeyRelation[Tournament] = fields.ForeignKeyField( - "models.Tournament", related_name="events", description="The Tournament this happens in" + "models.Tournament", + related_name="events", + description="The Tournament this happens in", + on_delete=fields.RESTRICT, ) class Meta: diff --git a/examples/relations.py b/examples/relations.py index caf92fbf0..e874aa2aa 100644 --- a/examples/relations.py +++ b/examples/relations.py @@ -25,7 +25,7 @@ class Event(Model): id = fields.IntField(primary_key=True) name = fields.TextField() tournament: fields.ForeignKeyRelation[Tournament] = fields.ForeignKeyField( - "models.Tournament", related_name="events" + "models.Tournament", related_name="events", on_delete=fields.RESTRICT ) participants: fields.ManyToManyRelation["Team"] = fields.ManyToManyField( "models.Team", related_name="events", through="event_team" diff --git a/examples/relations_recursive.py b/examples/relations_recursive.py index 7a34f04ca..3722ecee5 100644 --- a/examples/relations_recursive.py +++ b/examples/relations_recursive.py @@ -17,7 +17,7 @@ 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=fields.RESTRICT ) team_members: fields.ReverseRelation["Employee"] diff --git a/examples/relations_with_unique.py b/examples/relations_with_unique.py index b45d7de2a..c2502bf23 100644 --- a/examples/relations_with_unique.py +++ b/examples/relations_with_unique.py @@ -23,7 +23,7 @@ class Student(Model): id = fields.IntField(primary_key=True) name = fields.TextField() school: fields.ForeignKeyRelation[School] = fields.ForeignKeyField( - "models.School", related_name="students", to_field="id" + "models.School", related_name="students", to_field="id", on_delete=fields.RESTRICT ) diff --git a/examples/schema_create.py b/examples/schema_create.py index 40f7a5a47..0d54bf688 100644 --- a/examples/schema_create.py +++ b/examples/schema_create.py @@ -22,7 +22,10 @@ class Event(Model): id = fields.IntField(primary_key=True, description="Event ID") name = fields.CharField(max_length=255, unique=True) tournament: fields.ForeignKeyRelation[Tournament] = fields.ForeignKeyField( - "models.Tournament", related_name="events", description="FK to tournament" + "models.Tournament", + related_name="events", + description="FK to tournament", + on_delete=fields.RESTRICT, ) participants: fields.ManyToManyRelation["Team"] = fields.ManyToManyField( "models.Team", diff --git a/tests/model_setup/model_bad_rel1.py b/tests/model_setup/model_bad_rel1.py index 50d6309a9..558164d39 100644 --- a/tests/model_setup/model_bad_rel1.py +++ b/tests/model_setup/model_bad_rel1.py @@ -12,5 +12,5 @@ class Tournament(Model): class Event(Model): tournament: fields.ForeignKeyRelation[Tournament] = fields.ForeignKeyField( - "app.Tournament", related_name="events" + "app.Tournament", related_name="events", on_delete=fields.RESTRICT ) diff --git a/tests/model_setup/model_bad_rel2.py b/tests/model_setup/model_bad_rel2.py index 9a324f0c7..3d08acc15 100644 --- a/tests/model_setup/model_bad_rel2.py +++ b/tests/model_setup/model_bad_rel2.py @@ -17,5 +17,5 @@ class Tournament(Model): class Event(Model): tournament: fields.ForeignKeyRelation[Any] = fields.ForeignKeyField( - "models.Tour", related_name="events" + "models.Tour", related_name="events", on_delete=fields.RESTRICT ) diff --git a/tests/model_setup/model_bad_rel3.py b/tests/model_setup/model_bad_rel3.py index d75123039..66c7b78e8 100644 --- a/tests/model_setup/model_bad_rel3.py +++ b/tests/model_setup/model_bad_rel3.py @@ -13,5 +13,5 @@ class Tournament(Model): class Event(Model): tournament: fields.ForeignKeyRelation[Tournament] = fields.ForeignKeyField( - "Tournament", related_name="events" + "Tournament", related_name="events", on_delete=fields.RESTRICT ) diff --git a/tests/model_setup/model_bad_rel4.py b/tests/model_setup/model_bad_rel4.py index 68857febd..ef5872049 100644 --- a/tests/model_setup/model_bad_rel4.py +++ b/tests/model_setup/model_bad_rel4.py @@ -13,5 +13,5 @@ class Tournament(Model): class Event(Model): tournament: fields.ForeignKeyRelation[Tournament] = fields.ForeignKeyField( - "models.app.Tournament", related_name="events" + "models.app.Tournament", related_name="events", on_delete=fields.RESTRICT ) diff --git a/tests/model_setup/model_bad_rel6.py b/tests/model_setup/model_bad_rel6.py index e2bd49285..fe8ccfc40 100644 --- a/tests/model_setup/model_bad_rel6.py +++ b/tests/model_setup/model_bad_rel6.py @@ -13,5 +13,5 @@ class Tournament(Model): class Event(Model): tournament: fields.ForeignKeyRelation[Tournament] = fields.ForeignKeyField( - "models.Tournament", related_name="events", to_field="uuid" + "models.Tournament", related_name="events", to_field="uuid", on_delete=fields.RESTRICT ) diff --git a/tests/model_setup/model_bad_rel7.py b/tests/model_setup/model_bad_rel7.py index 2ed5dbd0a..9a03faaa6 100644 --- a/tests/model_setup/model_bad_rel7.py +++ b/tests/model_setup/model_bad_rel7.py @@ -13,5 +13,5 @@ class Tournament(Model): class Event(Model): tournament: fields.ForeignKeyRelation[Tournament] = fields.ForeignKeyField( - "models.Tournament", related_name="events", to_field="uuids" + "models.Tournament", related_name="events", to_field="uuids", on_delete=fields.RESTRICT ) diff --git a/tests/model_setup/models_dup1.py b/tests/model_setup/models_dup1.py index 01b11b96d..ff278fa3c 100644 --- a/tests/model_setup/models_dup1.py +++ b/tests/model_setup/models_dup1.py @@ -12,11 +12,11 @@ class Tournament(Model): class Event(Model): tournament: fields.ForeignKeyRelation[Tournament] = fields.ForeignKeyField( - "models.Tournament", related_name="events" + "models.Tournament", related_name="events", on_delete=fields.RESTRICT ) class Party(Model): tournament: fields.ForeignKeyRelation[Tournament] = fields.ForeignKeyField( - "models.Tournament", related_name="events" + "models.Tournament", related_name="events", on_delete=fields.RESTRICT ) diff --git a/tests/schema/models_cyclic.py b/tests/schema/models_cyclic.py index 4de3e09db..8306d98fd 100644 --- a/tests/schema/models_cyclic.py +++ b/tests/schema/models_cyclic.py @@ -8,17 +8,17 @@ class One(Model): tournament: fields.ForeignKeyRelation["Two"] = fields.ForeignKeyField( - "models.Two", related_name="events" + "models.Two", related_name="events", on_delete=fields.RESTRICT ) class Two(Model): tournament: fields.ForeignKeyRelation["Three"] = fields.ForeignKeyField( - "models.Three", related_name="events" + "models.Three", related_name="events", on_delete=fields.RESTRICT ) class Three(Model): tournament: fields.ForeignKeyRelation[One] = fields.ForeignKeyField( - "models.One", related_name="events" + "models.One", related_name="events", on_delete=fields.RESTRICT ) diff --git a/tests/schema/models_fk_1.py b/tests/schema/models_fk_1.py index e3d0023ee..c459af5a2 100644 --- a/tests/schema/models_fk_1.py +++ b/tests/schema/models_fk_1.py @@ -11,4 +11,6 @@ class One(Model): - tournament: fields.ForeignKeyRelation[Any] = fields.ForeignKeyField("moo") + tournament: fields.ForeignKeyRelation[Any] = fields.ForeignKeyField( + "moo", on_delete=fields.RESTRICT + ) diff --git a/tests/schema/models_no_auto_create_m2m.py b/tests/schema/models_no_auto_create_m2m.py index e0c5daa9c..3927e68c6 100644 --- a/tests/schema/models_no_auto_create_m2m.py +++ b/tests/schema/models_no_auto_create_m2m.py @@ -20,7 +20,10 @@ class Event(Model): id = fields.BigIntField(primary_key=True, description="Event ID") name = fields.TextField() tournament: fields.ForeignKeyRelation[Tournament] = fields.ForeignKeyField( - "models.Tournament", related_name="events", description="FK to tournament" + "models.Tournament", + related_name="events", + description="FK to tournament", + on_delete=fields.RESTRICT, ) participants: fields.ManyToManyRelation["Team"] = fields.ManyToManyField( "models.Team", @@ -41,10 +44,10 @@ class Meta: class TeamEvent(Model): team: fields.ForeignKeyRelation["Team"] = fields.ForeignKeyField( - "models.Team", related_name="teams" + "models.Team", related_name="teams", on_delete=fields.RESTRICT ) event: fields.ForeignKeyRelation[Event] = fields.ForeignKeyField( - "models.Event", related_name="events" + "models.Event", related_name="events", on_delete=fields.RESTRICT ) score = fields.IntField() @@ -58,7 +61,7 @@ class Team(Model): name = fields.CharField(max_length=50, primary_key=True, description="The TEAM name (and PK)") key = fields.IntField() manager: fields.ForeignKeyNullableRelation["Team"] = fields.ForeignKeyField( - "models.Team", related_name="team_members", null=True + "models.Team", related_name="team_members", null=True, on_delete=fields.RESTRICT ) talks_to: fields.ManyToManyRelation["Team"] = fields.ManyToManyField( "models.Team", related_name="gets_talked_to" diff --git a/tests/schema/models_no_db_constraint.py b/tests/schema/models_no_db_constraint.py index d1d6b0d6f..b46fce5e0 100644 --- a/tests/schema/models_no_db_constraint.py +++ b/tests/schema/models_no_db_constraint.py @@ -23,6 +23,7 @@ class Event(Model): db_constraint=False, related_name="events", description="FK to tournament", + on_delete=fields.RESTRICT, ) participants: fields.ManyToManyRelation["Team"] = fields.ManyToManyField( "models.Team", @@ -45,7 +46,11 @@ class Team(Model): name = fields.CharField(max_length=50, primary_key=True, description="The TEAM name (and PK)") key = fields.IntField() manager: fields.ForeignKeyNullableRelation["Team"] = fields.ForeignKeyField( - "models.Team", db_constraint=False, related_name="team_members", null=True + "models.Team", + db_constraint=False, + related_name="team_members", + null=True, + on_delete=fields.RESTRICT, ) talks_to: fields.ManyToManyRelation["Team"] = fields.ManyToManyField( "models.Team", db_constraint=False, related_name="gets_talked_to" diff --git a/tests/schema/models_schema_create.py b/tests/schema/models_schema_create.py index 2814179bf..08bf3bbd5 100644 --- a/tests/schema/models_schema_create.py +++ b/tests/schema/models_schema_create.py @@ -22,7 +22,10 @@ class Event(Model): id = fields.BigIntField(primary_key=True, description="Event ID") name = fields.TextField() tournament: fields.ForeignKeyRelation[Tournament] = fields.ForeignKeyField( - "models.Tournament", related_name="events", description="FK to tournament" + "models.Tournament", + related_name="events", + description="FK to tournament", + on_delete=fields.RESTRICT, ) participants: fields.ManyToManyRelation["Team"] = fields.ManyToManyField( "models.Team", @@ -45,7 +48,7 @@ class Team(Model): name = fields.CharField(max_length=50, primary_key=True, description="The TEAM name (and PK)") key = fields.IntField() manager: fields.ForeignKeyNullableRelation["Team"] = fields.ForeignKeyField( - "models.Team", related_name="team_members", null=True + "models.Team", related_name="team_members", null=True, on_delete=fields.RESTRICT ) talks_to: fields.ManyToManyRelation["Team"] = fields.ManyToManyField( "models.Team", related_name="gets_talked_to" @@ -88,7 +91,11 @@ class SourceFields(Model): chars = fields.CharField(max_length=255, source_field="some_chars_table", db_index=True) fk: fields.ForeignKeyNullableRelation["SourceFields"] = fields.ForeignKeyField( - "models.SourceFields", related_name="team_members", null=True, source_field="fk_sometable" + "models.SourceFields", + related_name="team_members", + null=True, + source_field="fk_sometable", + on_delete=fields.RESTRICT, ) rel_to: fields.ManyToManyRelation["SourceFields"] = fields.ManyToManyField( @@ -116,9 +123,7 @@ class Employee(Model): id = fields.IntField(primary_key=True) name = fields.TextField() company: fields.ForeignKeyRelation[Company] = fields.ForeignKeyField( - "models.Company", - related_name="employees", - to_field="uuid", + "models.Company", related_name="employees", to_field="uuid", on_delete=fields.RESTRICT ) diff --git a/tests/schema/test_generate_schema.py b/tests/schema/test_generate_schema.py index acb88d506..c1db9200d 100644 --- a/tests/schema/test_generate_schema.py +++ b/tests/schema/test_generate_schema.py @@ -9,7 +9,7 @@ class TestGenerateSchema(test.SimpleTestCase): - safe_schema_sql = """ + safe_schema_sql = r""" CREATE TABLE IF NOT EXISTS "company" ( "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "name" TEXT NOT NULL, @@ -22,7 +22,7 @@ class TestGenerateSchema(test.SimpleTestCase): 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 + "company_id" CHAR(36) NOT NULL REFERENCES "company" ("uuid") ON DELETE RESTRICT ); CREATE TABLE IF NOT EXISTS "inheritedmodel" ( "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, @@ -35,13 +35,13 @@ class TestGenerateSchema(test.SimpleTestCase): 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 + "fk_sometable" INT REFERENCES "sometable" ("sometable_id") ON DELETE RESTRICT ); 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 + "manager_id" VARCHAR(50) REFERENCES "team" ("name") ON DELETE RESTRICT ) /* 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"); @@ -54,8 +54,8 @@ class TestGenerateSchema(test.SimpleTestCase): 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 */; + "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 */, @@ -64,7 +64,7 @@ class TestGenerateSchema(test.SimpleTestCase): "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 */, + "tournament_id" SMALLINT NOT NULL REFERENCES "tournament" ("tid") ON DELETE RESTRICT /* 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 */; @@ -140,7 +140,7 @@ async def test_minrelation(self): await self.init_for("tests.testmodels") sql = self.get_sql('"minrelation"') self.assertIn( - '"tournament_id" SMALLINT NOT NULL REFERENCES "tournament" ("id") ON DELETE CASCADE', + '"tournament_id" SMALLINT NOT NULL REFERENCES "tournament" ("id") ON DELETE RESTRICT', sql, ) self.assertNotIn("participants", sql) @@ -279,7 +279,7 @@ async def test_schema(self): sql = get_schema_sql(connections.get("default"), safe=False) self.assertEqual( sql.strip(), - """ + r""" CREATE TABLE "company" ( "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "name" TEXT NOT NULL, @@ -292,7 +292,7 @@ async def test_schema(self): CREATE TABLE "employee" ( "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "name" TEXT NOT NULL, - "company_id" CHAR(36) NOT NULL REFERENCES "company" ("uuid") ON DELETE CASCADE + "company_id" CHAR(36) NOT NULL REFERENCES "company" ("uuid") ON DELETE RESTRICT ); CREATE TABLE "inheritedmodel" ( "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, @@ -305,13 +305,13 @@ async def test_schema(self): CREATE TABLE "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 + "fk_sometable" INT REFERENCES "sometable" ("sometable_id") ON DELETE RESTRICT ); CREATE INDEX "idx_sometable_some_ch_3d69eb" ON "sometable" ("some_chars_table"); CREATE TABLE "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 + "manager_id" VARCHAR(50) REFERENCES "team" ("name") ON DELETE RESTRICT ) /* The TEAMS! */; CREATE INDEX "idx_team_manager_676134" ON "team" ("manager_id", "key"); CREATE INDEX "idx_team_manager_ef8f69" ON "team" ("manager_id", "name"); @@ -324,8 +324,8 @@ async def test_schema(self): CREATE TABLE "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 */; + "created" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP /* Created *\/'`\/* datetime */ +) /* What Tournaments *\/'`\/* we have */; CREATE INDEX "idx_tournament_name_6fe200" ON "tournament" ("name"); CREATE TABLE "event" ( "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL /* Event ID */, @@ -334,7 +334,7 @@ async def test_schema(self): "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 */, + "tournament_id" SMALLINT NOT NULL REFERENCES "tournament" ("tid") ON DELETE RESTRICT /* 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 */; @@ -378,7 +378,7 @@ async def test_m2m_no_auto_create(self): r"""CREATE TABLE "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 + "manager_id" VARCHAR(50) REFERENCES "team" ("name") ON DELETE RESTRICT ) /* The TEAMS! */; CREATE INDEX "idx_team_manager_676134" ON "team" ("manager_id", "key"); CREATE INDEX "idx_team_manager_ef8f69" ON "team" ("manager_id", "name"); @@ -395,15 +395,15 @@ async def test_m2m_no_auto_create(self): "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 */, + "tournament_id" SMALLINT NOT NULL REFERENCES "tournament" ("tid") ON DELETE RESTRICT /* 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 "teamevents" ( "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "score" INT NOT NULL, - "event_id" BIGINT NOT NULL REFERENCES "event" ("id") ON DELETE CASCADE, - "team_id" VARCHAR(50) NOT NULL REFERENCES "team" ("name") ON DELETE CASCADE, + "event_id" BIGINT NOT NULL REFERENCES "event" ("id") ON DELETE RESTRICT, + "team_id" VARCHAR(50) NOT NULL REFERENCES "team" ("name") ON DELETE RESTRICT, CONSTRAINT "uid_teamevents_team_id_9e89fc" UNIQUE ("team_id", "event_id") ) /* How participants relate */; CREATE TABLE "team_team" ( @@ -458,7 +458,7 @@ async def test_minrelation(self): sql = self.get_sql("`minrelation`") self.assertIn("`tournament_id` SMALLINT NOT NULL,", sql) self.assertIn( - "FOREIGN KEY (`tournament_id`) REFERENCES `tournament` (`id`) ON DELETE CASCADE", sql + "FOREIGN KEY (`tournament_id`) REFERENCES `tournament` (`id`) ON DELETE RESTRICT", sql ) self.assertNotIn("participants", sql) @@ -526,7 +526,7 @@ async def test_schema(self): sql = get_schema_sql(connections.get("default"), safe=False) self.assertEqual( sql.strip(), - """ + r""" CREATE TABLE `company` ( `id` INT NOT NULL PRIMARY KEY AUTO_INCREMENT, `name` LONGTEXT NOT NULL, @@ -540,7 +540,7 @@ async def test_schema(self): `id` INT NOT NULL PRIMARY KEY AUTO_INCREMENT, `name` LONGTEXT NOT NULL, `company_id` CHAR(36) NOT NULL, - CONSTRAINT `fk_employee_company_08999a42` FOREIGN KEY (`company_id`) REFERENCES `company` (`uuid`) ON DELETE CASCADE + CONSTRAINT `fk_employee_company_08999a42` FOREIGN KEY (`company_id`) REFERENCES `company` (`uuid`) ON DELETE RESTRICT ) CHARACTER SET utf8mb4; CREATE TABLE `inheritedmodel` ( `id` INT NOT NULL PRIMARY KEY AUTO_INCREMENT, @@ -554,14 +554,14 @@ async def test_schema(self): `sometable_id` INT NOT NULL PRIMARY KEY AUTO_INCREMENT, `some_chars_table` VARCHAR(255) NOT NULL, `fk_sometable` INT, - CONSTRAINT `fk_sometabl_sometabl_6efae9bd` FOREIGN KEY (`fk_sometable`) REFERENCES `sometable` (`sometable_id`) ON DELETE CASCADE, + CONSTRAINT `fk_sometabl_sometabl_6efae9bd` FOREIGN KEY (`fk_sometable`) REFERENCES `sometable` (`sometable_id`) ON DELETE RESTRICT, KEY `idx_sometable_some_ch_3d69eb` (`some_chars_table`) ) CHARACTER SET utf8mb4; CREATE TABLE `team` ( `name` VARCHAR(50) NOT NULL PRIMARY KEY COMMENT 'The TEAM name (and PK)', `key` INT NOT NULL, `manager_id` VARCHAR(50), - CONSTRAINT `fk_team_team_9c77cd8f` FOREIGN KEY (`manager_id`) REFERENCES `team` (`name`) ON DELETE CASCADE, + CONSTRAINT `fk_team_team_9c77cd8f` FOREIGN KEY (`manager_id`) REFERENCES `team` (`name`) ON DELETE RESTRICT, KEY `idx_team_manager_676134` (`manager_id`, `key`), KEY `idx_team_manager_ef8f69` (`manager_id`, `name`) ) CHARACTER SET utf8mb4 COMMENT='The TEAMS!'; @@ -571,13 +571,13 @@ async def test_schema(self): `street` VARCHAR(128) NOT NULL COMMENT 'Street Address', `team_id` VARCHAR(50) NOT NULL PRIMARY KEY, CONSTRAINT `fk_teamaddr_team_1c78d737` FOREIGN KEY (`team_id`) REFERENCES `team` (`name`) ON DELETE CASCADE -) CHARACTER SET utf8mb4 COMMENT='The Team\\'s address'; +) CHARACTER SET utf8mb4 COMMENT='The Team\'s address'; CREATE TABLE `tournament` ( `tid` SMALLINT NOT NULL PRIMARY KEY AUTO_INCREMENT, `name` VARCHAR(100) NOT NULL COMMENT 'Tournament name', - `created` DATETIME(6) NOT NULL COMMENT 'Created */\\'`/* datetime' DEFAULT CURRENT_TIMESTAMP(6), + `created` DATETIME(6) NOT NULL COMMENT 'Created */\'`/* datetime' DEFAULT CURRENT_TIMESTAMP(6), KEY `idx_tournament_name_6fe200` (`name`) -) CHARACTER SET utf8mb4 COMMENT='What Tournaments */\\'`/* we have'; +) CHARACTER SET utf8mb4 COMMENT='What Tournaments */\'`/* we have'; CREATE TABLE `event` ( `id` BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT COMMENT 'Event ID', `name` LONGTEXT NOT NULL, @@ -588,7 +588,7 @@ async def test_schema(self): `tournament_id` SMALLINT NOT NULL COMMENT 'FK to tournament', UNIQUE KEY `uid_event_name_c6f89f` (`name`, `prize`), UNIQUE KEY `uid_event_tournam_a5b730` (`tournament_id`, `key`), - CONSTRAINT `fk_event_tourname_51c2b82d` FOREIGN KEY (`tournament_id`) REFERENCES `tournament` (`tid`) ON DELETE CASCADE + CONSTRAINT `fk_event_tourname_51c2b82d` FOREIGN KEY (`tournament_id`) REFERENCES `tournament` (`tid`) ON DELETE RESTRICT ) CHARACTER SET utf8mb4 COMMENT='This table contains a list of all the events'; CREATE TABLE `venueinformation` ( `id` INT NOT NULL PRIMARY KEY AUTO_INCREMENT, @@ -632,7 +632,7 @@ async def test_schema_safe(self): return self.assertEqual( sql, - """ + r""" CREATE TABLE IF NOT EXISTS `company` ( `id` INT NOT NULL PRIMARY KEY AUTO_INCREMENT, `name` LONGTEXT NOT NULL, @@ -646,7 +646,7 @@ async def test_schema_safe(self): `id` INT NOT NULL PRIMARY KEY AUTO_INCREMENT, `name` LONGTEXT NOT NULL, `company_id` CHAR(36) NOT NULL, - CONSTRAINT `fk_employee_company_08999a42` FOREIGN KEY (`company_id`) REFERENCES `company` (`uuid`) ON DELETE CASCADE + CONSTRAINT `fk_employee_company_08999a42` FOREIGN KEY (`company_id`) REFERENCES `company` (`uuid`) ON DELETE RESTRICT ) CHARACTER SET utf8mb4; CREATE TABLE IF NOT EXISTS `inheritedmodel` ( `id` INT NOT NULL PRIMARY KEY AUTO_INCREMENT, @@ -660,14 +660,14 @@ async def test_schema_safe(self): `sometable_id` INT NOT NULL PRIMARY KEY AUTO_INCREMENT, `some_chars_table` VARCHAR(255) NOT NULL, `fk_sometable` INT, - CONSTRAINT `fk_sometabl_sometabl_6efae9bd` FOREIGN KEY (`fk_sometable`) REFERENCES `sometable` (`sometable_id`) ON DELETE CASCADE, + CONSTRAINT `fk_sometabl_sometabl_6efae9bd` FOREIGN KEY (`fk_sometable`) REFERENCES `sometable` (`sometable_id`) ON DELETE RESTRICT, KEY `idx_sometable_some_ch_3d69eb` (`some_chars_table`) ) CHARACTER SET utf8mb4; CREATE TABLE IF NOT EXISTS `team` ( `name` VARCHAR(50) NOT NULL PRIMARY KEY COMMENT 'The TEAM name (and PK)', `key` INT NOT NULL, `manager_id` VARCHAR(50), - CONSTRAINT `fk_team_team_9c77cd8f` FOREIGN KEY (`manager_id`) REFERENCES `team` (`name`) ON DELETE CASCADE, + CONSTRAINT `fk_team_team_9c77cd8f` FOREIGN KEY (`manager_id`) REFERENCES `team` (`name`) ON DELETE RESTRICT, KEY `idx_team_manager_676134` (`manager_id`, `key`), KEY `idx_team_manager_ef8f69` (`manager_id`, `name`) ) CHARACTER SET utf8mb4 COMMENT='The TEAMS!'; @@ -677,13 +677,13 @@ async def test_schema_safe(self): `street` VARCHAR(128) NOT NULL COMMENT 'Street Address', `team_id` VARCHAR(50) NOT NULL PRIMARY KEY, CONSTRAINT `fk_teamaddr_team_1c78d737` FOREIGN KEY (`team_id`) REFERENCES `team` (`name`) ON DELETE CASCADE -) CHARACTER SET utf8mb4 COMMENT='The Team\\'s address'; +) CHARACTER SET utf8mb4 COMMENT='The Team\'s address'; CREATE TABLE IF NOT EXISTS `tournament` ( `tid` SMALLINT NOT NULL PRIMARY KEY AUTO_INCREMENT, `name` VARCHAR(100) NOT NULL COMMENT 'Tournament name', - `created` DATETIME(6) NOT NULL COMMENT 'Created */\\'`/* datetime' DEFAULT CURRENT_TIMESTAMP(6), + `created` DATETIME(6) NOT NULL COMMENT 'Created */\'`/* datetime' DEFAULT CURRENT_TIMESTAMP(6), KEY `idx_tournament_name_6fe200` (`name`) -) CHARACTER SET utf8mb4 COMMENT='What Tournaments */\\'`/* we have'; +) CHARACTER SET utf8mb4 COMMENT='What Tournaments */\'`/* we have'; CREATE TABLE IF NOT EXISTS `event` ( `id` BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT COMMENT 'Event ID', `name` LONGTEXT NOT NULL, @@ -694,7 +694,7 @@ async def test_schema_safe(self): `tournament_id` SMALLINT NOT NULL COMMENT 'FK to tournament', UNIQUE KEY `uid_event_name_c6f89f` (`name`, `prize`), UNIQUE KEY `uid_event_tournam_a5b730` (`tournament_id`, `key`), - CONSTRAINT `fk_event_tourname_51c2b82d` FOREIGN KEY (`tournament_id`) REFERENCES `tournament` (`tid`) ON DELETE CASCADE + CONSTRAINT `fk_event_tourname_51c2b82d` FOREIGN KEY (`tournament_id`) REFERENCES `tournament` (`tid`) ON DELETE RESTRICT ) CHARACTER SET utf8mb4 COMMENT='This table contains a list of all the events'; CREATE TABLE IF NOT EXISTS `venueinformation` ( `id` INT NOT NULL PRIMARY KEY AUTO_INCREMENT, @@ -766,7 +766,7 @@ async def test_m2m_no_auto_create(self): `name` VARCHAR(50) NOT NULL PRIMARY KEY COMMENT 'The TEAM name (and PK)', `key` INT NOT NULL, `manager_id` VARCHAR(50), - CONSTRAINT `fk_team_team_9c77cd8f` FOREIGN KEY (`manager_id`) REFERENCES `team` (`name`) ON DELETE CASCADE, + CONSTRAINT `fk_team_team_9c77cd8f` FOREIGN KEY (`manager_id`) REFERENCES `team` (`name`) ON DELETE RESTRICT, KEY `idx_team_manager_676134` (`manager_id`, `key`), KEY `idx_team_manager_ef8f69` (`manager_id`, `name`) ) CHARACTER SET utf8mb4 COMMENT='The TEAMS!'; @@ -786,7 +786,7 @@ async def test_m2m_no_auto_create(self): `tournament_id` SMALLINT NOT NULL COMMENT 'FK to tournament', UNIQUE KEY `uid_event_name_c6f89f` (`name`, `prize`), UNIQUE KEY `uid_event_tournam_a5b730` (`tournament_id`, `key`), - CONSTRAINT `fk_event_tourname_51c2b82d` FOREIGN KEY (`tournament_id`) REFERENCES `tournament` (`tid`) ON DELETE CASCADE + CONSTRAINT `fk_event_tourname_51c2b82d` FOREIGN KEY (`tournament_id`) REFERENCES `tournament` (`tid`) ON DELETE RESTRICT ) CHARACTER SET utf8mb4 COMMENT='This table contains a list of all the events'; CREATE TABLE `teamevents` ( `id` INT NOT NULL PRIMARY KEY AUTO_INCREMENT, @@ -794,8 +794,8 @@ async def test_m2m_no_auto_create(self): `event_id` BIGINT NOT NULL, `team_id` VARCHAR(50) NOT NULL, UNIQUE KEY `uid_teamevents_team_id_9e89fc` (`team_id`, `event_id`), - CONSTRAINT `fk_teameven_event_9d3bac2d` FOREIGN KEY (`event_id`) REFERENCES `event` (`id`) ON DELETE CASCADE, - CONSTRAINT `fk_teameven_team_dc3bc201` FOREIGN KEY (`team_id`) REFERENCES `team` (`name`) ON DELETE CASCADE + CONSTRAINT `fk_teameven_event_9d3bac2d` FOREIGN KEY (`event_id`) REFERENCES `event` (`id`) ON DELETE RESTRICT, + CONSTRAINT `fk_teameven_team_dc3bc201` FOREIGN KEY (`team_id`) REFERENCES `team` (`name`) ON DELETE RESTRICT ) CHARACTER SET utf8mb4 COMMENT='How participants relate'; CREATE TABLE `team_team` ( `team_rel_id` VARCHAR(50) NOT NULL, @@ -902,7 +902,7 @@ async def test_schema(self): CREATE TABLE "employee" ( "id" SERIAL NOT NULL PRIMARY KEY, "name" TEXT NOT NULL, - "company_id" UUID NOT NULL REFERENCES "company" ("uuid") ON DELETE CASCADE + "company_id" UUID NOT NULL REFERENCES "company" ("uuid") ON DELETE RESTRICT ); CREATE TABLE "inheritedmodel" ( "id" SERIAL NOT NULL PRIMARY KEY, @@ -915,13 +915,13 @@ async def test_schema(self): CREATE TABLE "sometable" ( "sometable_id" SERIAL NOT NULL PRIMARY KEY, "some_chars_table" VARCHAR(255) NOT NULL, - "fk_sometable" INT REFERENCES "sometable" ("sometable_id") ON DELETE CASCADE + "fk_sometable" INT REFERENCES "sometable" ("sometable_id") ON DELETE RESTRICT ); CREATE INDEX "idx_sometable_some_ch_3d69eb" ON "sometable" ("some_chars_table"); CREATE TABLE "team" ( "name" VARCHAR(50) NOT NULL PRIMARY KEY, "key" INT NOT NULL, - "manager_id" VARCHAR(50) REFERENCES "team" ("name") ON DELETE CASCADE + "manager_id" VARCHAR(50) REFERENCES "team" ("name") ON DELETE RESTRICT ); CREATE INDEX "idx_team_manager_676134" ON "team" ("manager_id", "key"); CREATE INDEX "idx_team_manager_ef8f69" ON "team" ("manager_id", "name"); @@ -953,7 +953,7 @@ async def test_schema(self): "prize" DECIMAL(10,2), "token" VARCHAR(100) NOT NULL UNIQUE, "key" VARCHAR(100) NOT NULL, - "tournament_id" SMALLINT NOT NULL REFERENCES "tournament" ("tid") ON DELETE CASCADE, + "tournament_id" SMALLINT NOT NULL REFERENCES "tournament" ("tid") ON DELETE RESTRICT, CONSTRAINT "uid_event_name_c6f89f" UNIQUE ("name", "prize"), CONSTRAINT "uid_event_tournam_a5b730" UNIQUE ("tournament_id", "key") ); @@ -1007,7 +1007,7 @@ async def test_schema_safe(self): CREATE TABLE IF NOT EXISTS "employee" ( "id" SERIAL NOT NULL PRIMARY KEY, "name" TEXT NOT NULL, - "company_id" UUID NOT NULL REFERENCES "company" ("uuid") ON DELETE CASCADE + "company_id" UUID NOT NULL REFERENCES "company" ("uuid") ON DELETE RESTRICT ); CREATE TABLE IF NOT EXISTS "inheritedmodel" ( "id" SERIAL NOT NULL PRIMARY KEY, @@ -1020,13 +1020,13 @@ async def test_schema_safe(self): CREATE TABLE IF NOT EXISTS "sometable" ( "sometable_id" SERIAL NOT NULL PRIMARY KEY, "some_chars_table" VARCHAR(255) NOT NULL, - "fk_sometable" INT REFERENCES "sometable" ("sometable_id") ON DELETE CASCADE + "fk_sometable" INT REFERENCES "sometable" ("sometable_id") ON DELETE RESTRICT ); 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, "key" INT NOT NULL, - "manager_id" VARCHAR(50) REFERENCES "team" ("name") ON DELETE CASCADE + "manager_id" VARCHAR(50) REFERENCES "team" ("name") ON DELETE RESTRICT ); 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"); @@ -1058,7 +1058,7 @@ async def test_schema_safe(self): "prize" DECIMAL(10,2), "token" VARCHAR(100) NOT NULL UNIQUE, "key" VARCHAR(100) NOT NULL, - "tournament_id" SMALLINT NOT NULL REFERENCES "tournament" ("tid") ON DELETE CASCADE, + "tournament_id" SMALLINT NOT NULL REFERENCES "tournament" ("tid") ON DELETE RESTRICT, CONSTRAINT "uid_event_name_c6f89f" UNIQUE ("name", "prize"), CONSTRAINT "uid_event_tournam_a5b730" UNIQUE ("tournament_id", "key") ); @@ -1150,7 +1150,7 @@ async def test_m2m_no_auto_create(self): r"""CREATE TABLE "team" ( "name" VARCHAR(50) NOT NULL PRIMARY KEY, "key" INT NOT NULL, - "manager_id" VARCHAR(50) REFERENCES "team" ("name") ON DELETE CASCADE + "manager_id" VARCHAR(50) REFERENCES "team" ("name") ON DELETE RESTRICT ); CREATE INDEX "idx_team_manager_676134" ON "team" ("manager_id", "key"); CREATE INDEX "idx_team_manager_ef8f69" ON "team" ("manager_id", "name"); @@ -1172,7 +1172,7 @@ async def test_m2m_no_auto_create(self): "prize" DECIMAL(10,2), "token" VARCHAR(100) NOT NULL UNIQUE, "key" VARCHAR(100) NOT NULL, - "tournament_id" SMALLINT NOT NULL REFERENCES "tournament" ("tid") ON DELETE CASCADE, + "tournament_id" SMALLINT NOT NULL REFERENCES "tournament" ("tid") ON DELETE RESTRICT, CONSTRAINT "uid_event_name_c6f89f" UNIQUE ("name", "prize"), CONSTRAINT "uid_event_tournam_a5b730" UNIQUE ("tournament_id", "key") ); @@ -1183,8 +1183,8 @@ async def test_m2m_no_auto_create(self): CREATE TABLE "teamevents" ( "id" SERIAL NOT NULL PRIMARY KEY, "score" INT NOT NULL, - "event_id" BIGINT NOT NULL REFERENCES "event" ("id") ON DELETE CASCADE, - "team_id" VARCHAR(50) NOT NULL REFERENCES "team" ("name") ON DELETE CASCADE, + "event_id" BIGINT NOT NULL REFERENCES "event" ("id") ON DELETE RESTRICT, + "team_id" VARCHAR(50) NOT NULL REFERENCES "team" ("name") ON DELETE RESTRICT, CONSTRAINT "uid_teamevents_team_id_9e89fc" UNIQUE ("team_id", "event_id") ); COMMENT ON TABLE "teamevents" IS 'How participants relate'; diff --git a/tests/test_early_init.py b/tests/test_early_init.py index 87dbd4015..d9d129abd 100644 --- a/tests/test_early_init.py +++ b/tests/test_early_init.py @@ -28,7 +28,7 @@ class Event(Model): name = fields.CharField(max_length=255) created_at = fields.DatetimeField(auto_now_add=True) tournament: fields.ForeignKeyNullableRelation[Tournament] = fields.ForeignKeyField( - "models.Tournament", related_name="events", null=True + "models.Tournament", related_name="events", null=True, on_delete=fields.RESTRICT ) class Meta: @@ -152,7 +152,7 @@ async def test_early_init(self): "docstring": None, "constraints": {}, "raw_field": None, - "on_delete": "CASCADE", + "on_delete": "RESTRICT", "db_constraint": True, } ], @@ -316,7 +316,7 @@ async def test_early_init(self): "python_type": "models.Tournament", "generated": False, "nullable": True, - "on_delete": "CASCADE", + "on_delete": "RESTRICT", "unique": False, "indexed": False, "default": None, diff --git a/tests/testmodels.py b/tests/testmodels.py index c0b95f352..b0005e375 100644 --- a/tests/testmodels.py +++ b/tests/testmodels.py @@ -53,7 +53,7 @@ class Author(Model): class Book(Model): name = fields.CharField(max_length=255) author: fields.ForeignKeyRelation[Author] = fields.ForeignKeyField( - "models.Author", related_name="books" + "models.Author", related_name="books", on_delete=fields.RESTRICT ) rating = fields.FloatField() subject = fields.CharField(max_length=255, null=True) @@ -62,7 +62,7 @@ class Book(Model): class BookNoConstraint(Model): name = fields.CharField(max_length=255) author: fields.ForeignKeyRelation[Author] = fields.ForeignKeyField( - "models.Author", db_constraint=False + "models.Author", db_constraint=False, on_delete=fields.RESTRICT ) rating = fields.FloatField() @@ -107,10 +107,10 @@ class Event(Model): name = fields.TextField() #: What tournaments is a happenin' tournament: fields.ForeignKeyRelation[Tournament] = fields.ForeignKeyField( - "models.Tournament", related_name="events" + "models.Tournament", related_name="events", on_delete=fields.RESTRICT ) reporter: fields.ForeignKeyNullableRelation[Reporter] = fields.ForeignKeyField( - "models.Reporter", null=True + "models.Reporter", null=True, on_delete=fields.RESTRICT ) participants: fields.ManyToManyRelation[Team] = fields.ManyToManyField( "models.Team", @@ -140,12 +140,16 @@ class ModelTestPydanticMetaBackwardRelations2(Model): ... class ModelTestPydanticMetaBackwardRelations3(Model): one: fields.ForeignKeyRelation[ModelTestPydanticMetaBackwardRelations1] = ( fields.ForeignKeyField( - "models.ModelTestPydanticMetaBackwardRelations1", related_name="threes" + "models.ModelTestPydanticMetaBackwardRelations1", + related_name="threes", + on_delete=fields.RESTRICT, ) ) two: fields.ForeignKeyRelation[ModelTestPydanticMetaBackwardRelations2] = ( fields.ForeignKeyField( - "models.ModelTestPydanticMetaBackwardRelations2", related_name="threes" + "models.ModelTestPydanticMetaBackwardRelations2", + related_name="threes", + on_delete=fields.RESTRICT, ) ) @@ -156,7 +160,7 @@ class Node(Model): class Tree(Model): parent: fields.ForeignKeyRelation[Node] = fields.ForeignKeyField( - "models.Node", related_name="parent_trees" + "models.Node", related_name="parent_trees", on_delete=fields.RESTRICT ) child: fields.ForeignKeyRelation[Node] = fields.ForeignKeyField( "models.Node", related_name="children_trees", on_delete=NO_ACTION @@ -368,7 +372,9 @@ class UUIDFields(Model): class MinRelation(Model): id = fields.IntField(primary_key=True) - tournament: fields.ForeignKeyRelation[Tournament] = fields.ForeignKeyField("models.Tournament") + tournament: fields.ForeignKeyRelation[Tournament] = fields.ForeignKeyField( + "models.Tournament", on_delete=fields.RESTRICT + ) participants: fields.ManyToManyRelation[Team] = fields.ManyToManyField("models.Team") @@ -411,7 +417,9 @@ class Meta: class UniqueTogetherFieldsWithFK(Model): id = fields.IntField(primary_key=True) text = fields.CharField(max_length=64) - tournament: fields.ForeignKeyRelation[Tournament] = fields.ForeignKeyField("models.Tournament") + tournament: fields.ForeignKeyRelation[Tournament] = fields.ForeignKeyField( + "models.Tournament", on_delete=fields.RESTRICT + ) class Meta: unique_together = ("text", "tournament") @@ -433,7 +441,7 @@ class UUIDFkRelatedModel(Model): id = fields.UUIDField(primary_key=True) name = fields.CharField(max_length=50, null=True) model: fields.ForeignKeyRelation[UUIDPkModel] = fields.ForeignKeyField( - "models.UUIDPkModel", related_name="children" + "models.UUIDPkModel", related_name="children", on_delete=fields.RESTRICT ) @@ -441,7 +449,7 @@ class UUIDFkRelatedNullModel(Model): id = fields.UUIDField(primary_key=True) name = fields.CharField(max_length=50, null=True) model: fields.ForeignKeyNullableRelation[UUIDPkModel] = fields.ForeignKeyField( - "models.UUIDPkModel", related_name=False, null=True + "models.UUIDPkModel", related_name=False, null=True, on_delete=fields.RESTRICT ) parent: fields.OneToOneNullableRelation[UUIDPkModel] = fields.OneToOneField( "models.UUIDPkModel", related_name=False, null=True, on_delete=NO_ACTION @@ -467,7 +475,10 @@ class UUIDFkRelatedSourceModel(Model): id = fields.UUIDField(primary_key=True, source_field="b") name = fields.CharField(max_length=50, null=True, source_field="c") model: fields.ForeignKeyRelation[UUIDPkSourceModel] = fields.ForeignKeyField( - "models.UUIDPkSourceModel", related_name="children", source_field="d" + "models.UUIDPkSourceModel", + related_name="children", + source_field="d", + on_delete=fields.RESTRICT, ) class Meta: @@ -482,6 +493,7 @@ class UUIDFkRelatedNullSourceModel(Model): related_name="children_null", source_field="k", null=True, + on_delete=fields.RESTRICT, ) class Meta: @@ -508,7 +520,7 @@ class CharPkModel(Model): class CharFkRelatedModel(Model): model: fields.ForeignKeyRelation[CharPkModel] = fields.ForeignKeyField( - "models.CharPkModel", related_name="children" + "models.CharPkModel", related_name="children", on_delete=fields.RESTRICT ) @@ -774,7 +786,7 @@ class Meta: class FKToDefaultOrdered(Model): link: fields.ForeignKeyRelation[DefaultOrdered] = fields.ForeignKeyField( - "models.DefaultOrdered", related_name="related" + "models.DefaultOrdered", related_name="related", on_delete=fields.RESTRICT ) value = fields.IntField() @@ -813,7 +825,7 @@ class Student(Model): id = fields.IntField(primary_key=True) name = fields.TextField() school: fields.ForeignKeyRelation[School] = fields.ForeignKeyField( - "models.School", related_name="students", to_field="id" + "models.School", related_name="students", to_field="id", on_delete=fields.RESTRICT ) @@ -938,7 +950,7 @@ class Single(Model): id = fields.IntField(primary_key=True) extra: fields.ForeignKeyNullableRelation[Extra] = fields.ForeignKeyField( - "models.Extra", related_name="singles", null=True + "models.Extra", related_name="singles", null=True, on_delete=fields.RESTRICT ) @@ -949,7 +961,7 @@ class Pair(Model): id = fields.IntField(primary_key=True) left: fields.ForeignKeyNullableRelation[Single] = fields.ForeignKeyField( - "models.Single", related_name="lefts", null=True + "models.Single", related_name="lefts", null=True, on_delete=fields.RESTRICT ) right: fields.ForeignKeyNullableRelation[Single] = fields.ForeignKeyField( "models.Single", related_name="rights", null=True, on_delete=NO_ACTION diff --git a/tests/testmodels_mysql.py b/tests/testmodels_mysql.py index 93d67088d..c99f7e74f 100644 --- a/tests/testmodels_mysql.py +++ b/tests/testmodels_mysql.py @@ -15,7 +15,7 @@ class UUIDFkRelatedModel(Model): id = mysql_fields.UUIDField(primary_key=True) name = fields.CharField(max_length=50, null=True) model: fields.ForeignKeyRelation[UUIDPkModel] = fields.ForeignKeyField( - "models.UUIDPkModel", related_name="children" + "models.UUIDPkModel", related_name="children", on_delete=fields.RESTRICT ) @@ -23,7 +23,7 @@ class UUIDFkRelatedNullModel(Model): id = mysql_fields.UUIDField(primary_key=True) name = fields.CharField(max_length=50, null=True) model: fields.ForeignKeyNullableRelation[UUIDPkModel] = fields.ForeignKeyField( - "models.UUIDPkModel", related_name=False, null=True + "models.UUIDPkModel", related_name=False, null=True, on_delete=fields.RESTRICT ) parent: fields.OneToOneNullableRelation[UUIDPkModel] = fields.OneToOneField( "models.UUIDPkModel", related_name=False, null=True, on_delete=fields.NO_ACTION @@ -49,7 +49,10 @@ class UUIDFkRelatedSourceModel(Model): id = mysql_fields.UUIDField(primary_key=True, source_field="b") name = fields.CharField(max_length=50, null=True, source_field="c") model: fields.ForeignKeyRelation[UUIDPkSourceModel] = fields.ForeignKeyField( - "models.UUIDPkSourceModel", related_name="children", source_field="d" + "models.UUIDPkSourceModel", + related_name="children", + source_field="d", + on_delete=fields.RESTRICT, ) class Meta: @@ -60,7 +63,11 @@ class UUIDFkRelatedNullSourceModel(Model): id = mysql_fields.UUIDField(primary_key=True, source_field="i") name = fields.CharField(max_length=50, null=True, source_field="j") model: fields.ForeignKeyNullableRelation[UUIDPkSourceModel] = fields.ForeignKeyField( - "models.UUIDPkSourceModel", related_name="children_null", source_field="k", null=True + "models.UUIDPkSourceModel", + related_name="children_null", + source_field="k", + null=True, + on_delete=fields.RESTRICT, ) class Meta: diff --git a/tests/utils/test_describe_model.py b/tests/utils/test_describe_model.py index 193306c68..5b7eda93a 100644 --- a/tests/utils/test_describe_model.py +++ b/tests/utils/test_describe_model.py @@ -91,7 +91,7 @@ def test_describe_field_noninit(self): ) def test_describe_relfield_noninit_ser(self): - field = fields.ForeignKeyField("a.b") + field = fields.ForeignKeyField("a.b", on_delete=fields.RESTRICT) self.assertEqual( field.describe(serializable=True), { @@ -100,7 +100,7 @@ def test_describe_relfield_noninit_ser(self): "python_type": "None", "generated": False, "nullable": False, - "on_delete": "CASCADE", + "on_delete": "RESTRICT", "unique": False, "indexed": False, "default": None, @@ -113,7 +113,7 @@ def test_describe_relfield_noninit_ser(self): ) def test_describe_relfield_noninit(self): - field = fields.ForeignKeyField("a.b") + field = fields.ForeignKeyField("a.b", on_delete=fields.RESTRICT) self.assertEqual( field.describe(serializable=False), { @@ -122,7 +122,7 @@ def test_describe_relfield_noninit(self): "python_type": None, "generated": False, "nullable": False, - "on_delete": "CASCADE", + "on_delete": "RESTRICT", "unique": False, "indexed": False, "default": None, @@ -1277,7 +1277,7 @@ def test_describe_model_uuidpk_relatednull(self): "indexed": False, "name": "model", "nullable": True, - "on_delete": "CASCADE", + "on_delete": "RESTRICT", "python_type": "models.UUIDPkModel", "raw_field": "model_id", "unique": False, diff --git a/tortoise/fields/relational.py b/tortoise/fields/relational.py index cae0cd79a..bac5e74a0 100644 --- a/tortoise/fields/relational.py +++ b/tortoise/fields/relational.py @@ -463,8 +463,8 @@ def OneToOneField( @overload def ForeignKeyField( model_name: str, + on_delete: OnDelete, related_name: str | None | Literal[False] = None, - on_delete: OnDelete = CASCADE, db_constraint: bool = True, *, null: Literal[True], @@ -475,8 +475,8 @@ def ForeignKeyField( @overload def ForeignKeyField( model_name: str, + on_delete: OnDelete, related_name: str | None | Literal[False] = None, - on_delete: OnDelete = CASCADE, db_constraint: bool = True, null: Literal[False] = False, **kwargs: Any, @@ -485,8 +485,8 @@ def ForeignKeyField( def ForeignKeyField( model_name: str, + on_delete: OnDelete, related_name: str | None | Literal[False] = None, - on_delete: OnDelete = CASCADE, db_constraint: bool = True, null: bool = False, **kwargs: Any,