Skip to content

Commit

Permalink
Fixed #26322 -- Consolidated lazy relationships details in docs/ref/m…
Browse files Browse the repository at this point in the history
…odels/fields.txt.

Reorganized docs to list and explain the types of lazy relationships
that can be defined in related fields.

Co-authored-by: Natalia <[email protected]>
  • Loading branch information
cliff688 and nessita authored Oct 17, 2024
1 parent bd3b1df commit 65f3cfc
Showing 1 changed file with 127 additions and 68 deletions.
195 changes: 127 additions & 68 deletions docs/ref/models/fields.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1628,80 +1628,25 @@ Django also defines a set of fields that represent relations.
.. class:: ForeignKey(to, on_delete, **options)

A many-to-one relationship. Requires two positional arguments: the class to
which the model is related and the :attr:`~ForeignKey.on_delete` option.

.. _recursive-relationships:

To create a recursive relationship -- an object that has a many-to-one
relationship with itself -- use ``models.ForeignKey('self',
on_delete=models.CASCADE)``.

.. _lazy-relationships:

If you need to create a relationship on a model that has not yet been defined,
you can use the name of the model, rather than the model object itself::

from django.db import models


class Car(models.Model):
manufacturer = models.ForeignKey(
"Manufacturer",
on_delete=models.CASCADE,
)
# ...


class Manufacturer(models.Model):
# ...
pass

Relationships defined this way on :ref:`abstract models
<abstract-base-classes>` are resolved when the model is subclassed as a
concrete model and are not relative to the abstract model's ``app_label``:

.. code-block:: python
:caption: ``products/models.py``

from django.db import models


class AbstractCar(models.Model):
manufacturer = models.ForeignKey("Manufacturer", on_delete=models.CASCADE)

class Meta:
abstract = True

.. code-block:: python
:caption: ``production/models.py``
which the model is related and the :attr:`~ForeignKey.on_delete` option::

from django.db import models
from products.models import AbstractCar


class Manufacturer(models.Model):
pass


class Car(AbstractCar):
pass

name = models.TextField()

# Car.manufacturer will point to `production.Manufacturer` here.

To refer to models defined in another application, you can explicitly specify
a model with the full application label. For example, if the ``Manufacturer``
model above is defined in another application called ``production``, you'd
need to use::

class Car(models.Model):
manufacturer = models.ForeignKey(
"production.Manufacturer",
on_delete=models.CASCADE,
)
manufacturer = models.ForeignKey(Manufacturer, on_delete=models.CASCADE)

This sort of reference, called a lazy relationship, can be useful when
resolving circular import dependencies between two applications.
The first positional argument can be either a concrete model class or a
:ref:`lazy reference <lazy-relationships>` to a model class.
:ref:`Recursive relationships <recursive-relationships>`, where a model has a
relationship with itself, are also supported.

See :attr:`ForeignKey.on_delete` for details on the second positional
argument.

A database index is automatically created on the ``ForeignKey``. You can
disable this by setting :attr:`~Field.db_index` to ``False``. You may want to
Expand All @@ -1714,9 +1659,9 @@ Database Representation

Behind the scenes, Django appends ``"_id"`` to the field name to create its
database column name. In the above example, the database table for the ``Car``
model will have a ``manufacturer_id`` column. (You can change this explicitly by
specifying :attr:`~Field.db_column`) However, your code should never have to
deal with the database column name, unless you write custom SQL. You'll always
model will have a ``manufacturer_id`` column. You can change this explicitly by
specifying :attr:`~Field.db_column`, however, your code should never have to
deal with the database column name (unless you write custom SQL). You'll always
deal with the field names of your model object.

.. _foreign-key-arguments:
Expand Down Expand Up @@ -2266,6 +2211,120 @@ accepted by :class:`ForeignKey`, plus one extra argument:
See :doc:`One-to-one relationships </topics/db/examples/one_to_one>` for usage
examples of ``OneToOneField``.

.. _lazy-relationships:

Lazy relationships
------------------

Lazy relationships allow referencing models by their names (as strings) or
creating recursive relationships. Strings can be used as the first argument in
any relationship field to reference models lazily. A lazy reference can be
either :ref:`recursive <recursive-relationships>`,
:ref:`relative <relative-relationships>` or
:ref:`absolute <absolute-relationships>`.

.. _recursive-relationships:

Recursive
~~~~~~~~~

To define a relationship where a model references itself, use ``"self"`` as the
first argument of the relationship field::

from django.db import models


class Manufacturer(models.Model):
name = models.TextField()
suppliers = models.ManyToManyField("self", symmetrical=False)


When used in an :ref:`abstract model <abstract-base-classes>`, the recursive
relationship resolves such that each concrete subclass references itself.

.. _relative-relationships:

Relative
~~~~~~~~

When a relationship needs to be created with a model that has not been defined
yet, it can be referenced by its name rather than the model object itself::

from django.db import models


class Car(models.Model):
manufacturer = models.ForeignKey(
"Manufacturer",
on_delete=models.CASCADE,
)


class Manufacturer(models.Model):
name = models.TextField()
suppliers = models.ManyToManyField("self", symmetrical=False)

Relationships defined this way on :ref:`abstract models
<abstract-base-classes>` are resolved when the model is subclassed as a
concrete model and are not relative to the abstract model's ``app_label``:

.. code-block:: python
:caption: ``products/models.py``

from django.db import models


class AbstractCar(models.Model):
manufacturer = models.ForeignKey("Manufacturer", on_delete=models.CASCADE)

class Meta:
abstract = True

.. code-block:: python
:caption: ``production/models.py``

from django.db import models
from products.models import AbstractCar


class Manufacturer(models.Model):
name = models.TextField()


class Car(AbstractCar):
pass

In this example, the ``Car.manufacturer`` relationship will resolve to
``production.Manufacturer``, as it points to the concrete model defined
within the ``production/models.py`` file.

.. admonition:: Reusable models with relative references

Relative references allow the creation of reusable abstract models with
relationships that can resolve to different implementations of the
referenced models in various subclasses across different applications.

.. _absolute-relationships:

Absolute
~~~~~~~~

Absolute references specify a model using its ``app_label`` and class name,
allowing for model references across different applications. This type of lazy
relationship can also help resolve circular imports.

For example, if the ``Manufacturer`` model is defined in another application
called ``thirdpartyapp``, it can be referenced as::

class Car(models.Model):
manufacturer = models.ForeignKey(
"thirdpartyapp``.Manufacturer",
on_delete=models.CASCADE,
)

Absolute references always point to the same model, even when used in an
:ref:`abstract model <abstract-base-classes>`.

Field API reference
===================

Expand Down

0 comments on commit 65f3cfc

Please sign in to comment.