From 0c487aa3a7b2417481bf48c1e5355c855873e210 Mon Sep 17 00:00:00 2001 From: Mariusz Felisiak Date: Sat, 18 Oct 2025 15:03:50 +0200 Subject: Fixed #21961 -- Added support for database-level delete options for ForeignKey. Thanks Simon Charette for pair programming. Co-authored-by: Nick Stefan Co-authored-by: Akash Kumar Sen <71623442+Akash-Kumar-Sen@users.noreply.github.com> Co-authored-by: Simon Charette --- docs/ref/checks.txt | 16 ++++++++++--- docs/ref/models/fields.txt | 53 +++++++++++++++++++++++++++++++++++++------ docs/ref/models/instances.txt | 10 +++++++- docs/ref/models/querysets.txt | 29 +++++++++++++++++------ docs/releases/6.1.txt | 15 ++++++++++++ 5 files changed, 105 insertions(+), 18 deletions(-) (limited to 'docs') diff --git a/docs/ref/checks.txt b/docs/ref/checks.txt index 7735eed478..c297938f45 100644 --- a/docs/ref/checks.txt +++ b/docs/ref/checks.txt @@ -299,9 +299,15 @@ Related fields referenced by a ``ForeignKey``. * **fields.E312**: The ``to_field`` ```` doesn't exist on the related model ``.``. -* **fields.E320**: Field specifies ``on_delete=SET_NULL``, but cannot be null. -* **fields.E321**: The field specifies ``on_delete=SET_DEFAULT``, but has no - default value. +* **fields.E320**: Field specifies ``on_delete=``, but cannot + be null. +* **fields.E321**: Field specifies ``on_delete=SET_DEFAULT``, but has no + ``default`` value. +* **fields.E322**: Field specifies ``on_delete=DB_SET_DEFAULT``, but has no + ``db_default`` value. +* **fields.E323**: Field specifies database/Python-level on_delete variant, but + referenced model uses python/database-level variant. +* **fields.E324**: ```` does not support ``DB_SET_DEFAULT``. * **fields.E330**: ``ManyToManyField``\s cannot be unique. * **fields.E331**: Field specifies a many-to-many relation through model ````, which has not been installed. @@ -446,6 +452,8 @@ Models * **models.E049**: ``constraints/indexes/unique_together`` refers to a ``ForeignObject`` ```` with multiple ``from_fields``, which is not supported for that option. +* **models.E050**: The model cannot have related fields with both + database-level and Python-level ``on_delete`` variants. Management Commands ------------------- @@ -921,6 +929,8 @@ The following checks are performed when a model contains a * **contenttypes.E004**: ```` is not a ``ForeignKey`` to ``contenttypes.ContentType``. * **contenttypes.E005**: Model names must be at most 100 characters. +* **contenttypes.E006**: ```` cannot use the database-level + ``on_delete`` variant. ``postgres`` ------------ diff --git a/docs/ref/models/fields.txt b/docs/ref/models/fields.txt index 5988d5dc06..0b62143cc0 100644 --- a/docs/ref/models/fields.txt +++ b/docs/ref/models/fields.txt @@ -1699,11 +1699,11 @@ relation works. .. attribute:: ForeignKey.on_delete - When an object referenced by a :class:`ForeignKey` is deleted, Django will - emulate the behavior of the SQL constraint specified by the - :attr:`on_delete` argument. For example, if you have a nullable - :class:`ForeignKey` and you want it to be set null when the referenced - object is deleted:: + When an object referenced by a :class:`ForeignKey` is deleted, the + referring objects need updating. The :attr:`on_delete` argument specifies + how this is done, and whether Django or your database makes the updates. + For example, if you have a nullable :class:`ForeignKey` and you want Django + to set it to ``None`` when the referenced object is deleted:: user = models.ForeignKey( User, @@ -1712,8 +1712,21 @@ relation works. null=True, ) - ``on_delete`` doesn't create an SQL constraint in the database. Support for - database-level cascade options :ticket:`may be implemented later <21961>`. + The possible values for :attr:`~ForeignKey.on_delete` are listed below. + Import them from :mod:`django.db.models`. The ``DB_*`` variants use the + database to prevent deletions or update referring objects, whilst the other + values make Django perform the relevant actions. + + The database variants are more efficient because they avoid fetching + related objects, but ``pre_delete`` and ``post_delete`` signals won't be + sent when ``DB_CASCADE`` is used. + + The database variants cannot be mixed with Python variants (other than + :attr:`DO_NOTHING`) in the same model and in models related to each other. + +.. versionchanged:: 6.1 + + Support for ``DB_*`` variants of the ``on_delete`` attribute was added. The possible values for :attr:`~ForeignKey.on_delete` are found in :mod:`django.db.models`: @@ -1729,6 +1742,13 @@ The possible values for :attr:`~ForeignKey.on_delete` are found in :data:`~django.db.models.signals.post_delete` signals are sent for all deleted objects. +* .. attribute:: DB_CASCADE + + .. versionadded:: 6.1 + + Cascade deletes. Database-level version of :attr:`CASCADE`: the database + deletes referred-to rows and the one containing the ``ForeignKey``. + * .. attribute:: PROTECT Prevent deletion of the referenced object by raising @@ -1782,11 +1802,30 @@ The possible values for :attr:`~ForeignKey.on_delete` are found in Set the :class:`ForeignKey` null; this is only possible if :attr:`~Field.null` is ``True``. +* .. attribute:: DB_SET_NULL + + .. versionadded:: 6.1 + + Set the :class:`ForeignKey` value to ``NULL``. This is only possible if + :attr:`~Field.null` is ``True``. Database-level version of + :attr:`SET_NULL`. + * .. attribute:: SET_DEFAULT Set the :class:`ForeignKey` to its default value; a default for the :class:`ForeignKey` must be set. +* .. attribute:: DB_SET_DEFAULT + + .. versionadded:: 6.1 + + Set the :class:`ForeignKey` value to its :attr:`Field.db_default` value, + which must be set. If a row in the referenced table is deleted, the foreign + key values in the referencing table will be updated to their + :attr:`Field.db_default` values. + + ``DB_SET_DEFAULT`` is not supported on MySQL and MariaDB. + * .. function:: SET() Set the :class:`ForeignKey` to the value passed to diff --git a/docs/ref/models/instances.txt b/docs/ref/models/instances.txt index 2ce8dc4a36..a8be767aaf 100644 --- a/docs/ref/models/instances.txt +++ b/docs/ref/models/instances.txt @@ -695,7 +695,11 @@ Issues an SQL ``DELETE`` for the object. This only deletes the object in the database; the Python instance will still exist and will still have data in its fields, except for the primary key set to ``None``. This method returns the number of objects deleted and a dictionary with the number of deletions per -object type. +object type. The return value will count instances from related models if +Django is emulating cascade behavior via Python :attr:`~ForeignKey.on_delete` +variants. Otherwise, for database variants such as +:attr:`~django.db.models.DB_CASCADE`, the return value will report only +instances of the :class:`.QuerySet`'s model. For more details, including how to delete objects in bulk, see :ref:`topics-db-queries-delete`. @@ -707,6 +711,10 @@ Sometimes with :ref:`multi-table inheritance ` you may want to delete only a child model's data. Specifying ``keep_parents=True`` will keep the parent model's data. +.. versionchanged:: 6.1 + + Support for the ``DB_*`` variants of ``on_delete`` attribute was added. + Pickling objects ================ diff --git a/docs/ref/models/querysets.txt b/docs/ref/models/querysets.txt index 3840a2f97e..164bc9ce54 100644 --- a/docs/ref/models/querysets.txt +++ b/docs/ref/models/querysets.txt @@ -3036,7 +3036,11 @@ unique field in the order that is specified without conflicts. For example:: Performs an SQL delete query on all rows in the :class:`.QuerySet` and returns the number of objects deleted and a dictionary with the number of -deletions per object type. +deletions per object type. The return value will count instances from related +models if Django is emulating cascade behavior via Python +:attr:`~django.db.models.ForeignKey.on_delete` variants. Otherwise, for +database variants such as :attr:`~django.db.models.DB_CASCADE`, the return +value will report only instances of the :class:`.QuerySet`'s model. The ``delete()`` is applied instantly. You cannot call ``delete()`` on a :class:`.QuerySet` that has had a slice taken or can otherwise no longer be @@ -3073,13 +3077,20 @@ The ``delete()`` method does a bulk delete and does not call any ``delete()`` methods on your models. It does, however, emit the :data:`~django.db.models.signals.pre_delete` and :data:`~django.db.models.signals.post_delete` signals for all deleted objects -(including cascaded deletions). +(including cascaded deletions). Signals won't be sent when ``DB_CASCADE`` is +used. Also, ``delete()`` doesn't return information about objects deleted from +database variants (``DB_*``) of the +:attr:`~django.db.models.ForeignKey.on_delete` argument, e.g. ``DB_CASCADE``. -Django needs 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 the +following cases: + +#. If related fields use ``DB_*`` options. +#. 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. ForeignKeys which are set to :attr:`~django.db.models.ForeignKey.on_delete` ``DO_NOTHING`` do not prevent taking the fast-path in deletion. @@ -3087,6 +3098,10 @@ ForeignKeys which are set to :attr:`~django.db.models.ForeignKey.on_delete` Note that the queries generated in object deletion is an implementation detail subject to change. +.. versionchanged:: 6.1 + + Support for the ``DB_*`` variants of ``on_delete`` attribute was added. + ``as_manager()`` ~~~~~~~~~~~~~~~~ diff --git a/docs/releases/6.1.txt b/docs/releases/6.1.txt index 80470dbcd6..d199423176 100644 --- a/docs/releases/6.1.txt +++ b/docs/releases/6.1.txt @@ -71,6 +71,21 @@ queries: See :doc:`fetch modes ` for more details. +Database-level delete options for ``ForeignKey.on_delete`` +---------------------------------------------------------- + +:attr:`.ForeignKey.on_delete` now supports database-level delete options: + +* :attr:`~django.db.models.DB_CASCADE` +* :attr:`~django.db.models.DB_SET_NULL` +* :attr:`~django.db.models.DB_SET_DEFAULT` + +These options handle deletion logic entirely within the database, using the SQL +``ON DELETE`` clause. They are thus 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. + Minor features -------------- -- cgit v1.3