Skip to content

feat: deprecate implicit on_delete default in ForeignKeyField, OneToOneField, ManyToManyField#2229

Open
Yad4o wants to merge 1 commit into
tortoise:developfrom
Yad4o:feat/on-delete-deprecation-warning
Open

feat: deprecate implicit on_delete default in ForeignKeyField, OneToOneField, ManyToManyField#2229
Yad4o wants to merge 1 commit into
tortoise:developfrom
Yad4o:feat/on-delete-deprecation-warning

Conversation

@Yad4o

@Yad4o Yad4o commented Jun 27, 2026

Copy link
Copy Markdown

Summary

Closes #1801.

ForeignKeyField, OneToOneField, and ManyToManyField silently defaulted on_delete to CASCADE. This is dangerous — a developer who forgets to set on_delete may inadvertently cascade-delete data they didn't intend to.

This PR adds a DeprecationWarning when on_delete is omitted, making the implicit behaviour visible and encouraging explicit intent. The default behaviour is preserved for now to avoid a breaking change.

Changes

  • Added _UNSET sentinel in tortoise/fields/relational.py to detect when on_delete is not passed
  • ForeignKeyFieldInstance.__init__: emits DeprecationWarning when on_delete is not explicitly provided
  • OneToOneFieldInstance.__init__: same (delegates to ForeignKeyFieldInstance)
  • ManyToManyFieldInstance.__init__: same
  • All three public factory functions (ForeignKeyField, OneToOneField, ManyToManyField) updated to use the sentinel as default
  • @overload signatures are unchanged (type checkers still see OnDelete = CASCADE) so no type-checking regressions

Before / After

# Before — silent CASCADE, no indication this is dangerous
class MyModel(Model):
    user = fields.ForeignKeyField("models.User")  # silently cascades on delete

# After — explicit warning shown at import/class-definition time
# DeprecationWarning: Not passing `on_delete` to ForeignKeyField is deprecated and will be an
# error in a future release. Pass `on_delete` explicitly (e.g. on_delete=fields.CASCADE).

# Correct usage going forward:
class MyModel(Model):
    user = fields.ForeignKeyField("models.User", on_delete=fields.CASCADE)

Notes

  • Backward compatible: existing code still works, just warns
  • The warning fires at model class definition time (not at query time), so it's easy to catch
  • Future release can make on_delete truly required by removing the sentinel fallback

Add a DeprecationWarning when `on_delete` is not explicitly passed to
`ForeignKeyField`, `OneToOneField`, or `ManyToManyField`. The current
silent default of `CASCADE` is dangerous as it can cause unintended
cascade deletes. Users should pass `on_delete` explicitly.

A sentinel `_UNSET` is used to distinguish "not passed" from a real
`CASCADE` value, so existing code continues to work but emits a warning.

Fixes tortoise#1801
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

on_delete should be required

1 participant