8000 Fixed #21961 -- Added support for database-level delete options for ForeignKey. by felixxm · Pull Request #19925 · django/django · GitHub
[go: up one dir, main page]

Skip to content

Conversation

felixxm
Copy link
Member
@felixxm felixxm commented Oct 7, 2025

@felixxm felixxm marked this pull request as draft October 7, 2025 09:54
@felixxm felixxm changed the title Fixed #21961 -- Added support for database level delete options for ForeignKey. [WiP] Fixed #21961 -- Added support for database level delete options for ForeignKey. Oct 7, 2025
@felixxm felixxm force-pushed the db-cascade-21961 branch 7 times, most recently from f816238 to febec5c Compare October 8, 2025 11:32
Copy link
Contributor
@LilyFirefly LilyFirefly left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A couple minor suggestions.

Copy link
Member
@jacobtylerwalls jacobtylerwalls left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking great. Took a look at docs and a first look at the implementation.

I think changes are pending to the system checks. When that's in, I'll look at that, the tests, the django.contrib changes, and do some manual testing.

@felixxm felixxm force-pushed the db-cascade-21961 branch 5 times, most recently from 68884c1 to 1ffc225 Compare October 9, 2025 14:12
@felixxm felixxm changed the title [WiP] Fixed #21961 -- Added support for database level delete options for ForeignKey. [WiP] Fixed #21961 -- Added support for database-level delete options for ForeignKey. Oct 9, 2025
@felixxm felixxm force-pushed the db-cascade-21961 branch 3 times, most recently from 4bc3363 to 9353da6 Compare October 10, 2025 10:34
@felixxm felixxm marked this pull request as ready for review October 10, 2025 11:26
@felixxm felixxm changed the title [WiP] Fixed #21961 -- Added support for database-level delete options for ForeignKey. Fixed #21961 -- Added support for database-level delete options for ForeignKey. Oct 10, 2025
@charettes
Copy link
Member

@felixxm as discussed, I just pushed the nested admin collector tests for DB_CASCADE and DB_RESTRICT here

from django.core.checks import Warning as DjangoWarning
from django.db import connection, models
from django.test.testcases import SimpleTestCase
from django.test import skipUnlessDBFeature
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not related to your changes but this module should be named test_related_fields, there's no such thing as a relative field 😅

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch 🎯 Let's rename it when this is merged.

@felixxm
Copy link
Member Author
felixxm commented Oct 10, 2025

@felixxm as discussed, I just pushed the nested admin collector tests for DB_CASCADE and DB_RESTRICT here

Thanks 👍 Merged 🎉

…oreignKey.

Thanks Simon Charette for pair programming.

Co-Authored-By: Nick Stefan <NickStefan12@gmail.com>
Co-Authored-By: Akash Kumar Sen <71623442+Akash-Kumar-Sen@users.noreply.github.com>
Co-Authored-By: Simon Charette <charette.s@gmail.com>
@felixxm
Copy link
Member Author
felixxm commented Oct 11, 2025

buildbot, test on oracle.

@felixxm
Copy link
Member Author
felixxm commented Oct 11, 2025

buildbot, test on oracle.

Comment on lines 187 to 189
def __init__(self, *args, **kwargs):
kwargs.setdefault("force_collection", True)
super().__init__(*args, **kwargs)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not put this in the signature?

Suggested change
def __init__(self, *args, **kwargs):
kwargs.setdefault("force_collection", True)
super().__init__(*args, **kwargs)
def __init__(self, *args, force_collection=True, **kwargs):
super().__init__(*args, force_collection=force_collection, **kwargs)

Comment on lines +2440 to +2441
"The model cannot have both related fields with "
"database-level and python-level on_delete variants.",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Grammar fix:

Suggested change
"The model cannot have both related fields with "
"database-level and python-level on_delete variants.",
"The model cannot have related fields with both "
"database-level and python-level on_delete variants.",

Comment on lines +3067 to +3071
If ``DB_*`` options are used, Django doesn't need to fetch objects into memory
to send signals and handle cascades. However, if there are no cascades and no
signals, then Django may take a fast-path and delete objects without fetching
into memory. For large deletes this can result in significantly reduced memory
usage. The amount of executed queries can be reduced, too.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The current edit doesn't really make sense: "However, ..." doesn't connect. My attempt to clarify:

Suggested change
If ``DB_*`` options are used, Django doesn't need to fetch objects into memory
to send signals and handle cascades. However, if there are no cascades and no
signals, then Django may take a fast-path and delete objects without fetching
into memory. For large deletes this can result in significantly reduced memory
usage. The amount of executed queries can be reduced, too.
Django won’t need to fetch objects into memory when deleting them in two cases:
1. If related fields use ``DB_*`` options.
2. If there are no cascades and no delete signal receivers.
In these cases, Django may take a fast path and delete objects without fetching
them, which can result in significantly reduced memory usage and fewer executed
queries.

Comment on lines +453 to +454
* **models.E049**: The model cannot have both related fields with
database-level and python-level ``on_delete`` variants.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
6D38
* **models.E049**: The model cannot have both related fields with
database-level and python-level ``on_delete`` variants.
* **models.E049**: The model cannot have related fields with both
database-level and python-level ``on_delete`` variants.

Comment on lines +39 to +41
The database variants are more efficient because they avoid fetching related
objects, but ``pre_delete`` and ``post_delete`` signals won't be sent when
:attr:`~django.db.models.DB_CASCADE` is used.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can spell out how they work a bit more, to help readers with some SQL knowledge make the connection, and to promote the feature.

Suggested change
The database variants are more efficient because they avoid fetching related
objects, but ``pre_delete`` and ``post_delete`` signals won't be sent when
:attr:`~django.db.models.DB_CASCADE` is used.
These options handle deletion logic entirely within the database, using the SQL
``ON DELETE`` clause. They are thus more more efficient than the existing
Python-level options, as Django does not need to load objects before deleting
them. As a consequence, the :attr:`~django.db.models.DB_CASCADE` option does
not trigger the ``pre_delete`` or ``post_delete`` signals.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

0