summaryrefslogtreecommitdiff
path: root/docs
diff options
context:
space:
mode:
authorBendeguz Csirmaz <csirmazbendeguz@gmail.com>2024-04-07 10:32:16 +0800
committerSarah Boyce <42296566+sarahboyce@users.noreply.github.com>2024-11-29 11:23:04 +0100
commit978aae4334fa71ba78a3e94408f0f3aebde8d07c (patch)
treedd1cc322769441a3dd28b952ce52e07c3f72f90a /docs
parent86661f2449fb0903f72b3522c68e146934013377 (diff)
Fixed #373 -- Added CompositePrimaryKey.
Thanks Lily Foote and Simon Charette for reviews and mentoring this Google Summer of Code 2024 project. Co-authored-by: Simon Charette <charette.s@gmail.com> Co-authored-by: Lily Foote <code@lilyf.org>
Diffstat (limited to 'docs')
-rw-r--r--docs/ref/checks.txt3
-rw-r--r--docs/ref/models/fields.txt19
-rw-r--r--docs/releases/5.2.txt19
-rw-r--r--docs/topics/composite-primary-key.txt183
-rw-r--r--docs/topics/index.txt1
5 files changed, 225 insertions, 0 deletions
diff --git a/docs/ref/checks.txt b/docs/ref/checks.txt
index 2308a854c7..b0a98bde28 100644
--- a/docs/ref/checks.txt
+++ b/docs/ref/checks.txt
@@ -181,6 +181,7 @@ Model fields
* **fields.E011**: ``<database>`` does not support default database values with
expressions (``db_default``).
* **fields.E012**: ``<expression>`` cannot be used in ``db_default``.
+* **fields.E013**: ``CompositePrimaryKey`` must be named ``pk``.
* **fields.E100**: ``AutoField``\s must set primary_key=True.
* **fields.E110**: ``BooleanField``\s do not accept null values. *This check
appeared before support for null values was added in Django 2.1.*
@@ -417,6 +418,8 @@ Models
* **models.W040**: ``<database>`` does not support indexes with non-key
columns.
* **models.E041**: ``constraints`` refers to the joined field ``<field name>``.
+* **models.E042**: ``<field name>`` cannot be included in the composite
+ primary key.
* **models.W042**: Auto-created primary key used when not defining a primary
key type, by default ``django.db.models.AutoField``.
* **models.W043**: ``<database>`` does not support indexes on expressions.
diff --git a/docs/ref/models/fields.txt b/docs/ref/models/fields.txt
index 07e86785d9..5b0f127c6f 100644
--- a/docs/ref/models/fields.txt
+++ b/docs/ref/models/fields.txt
@@ -707,6 +707,23 @@ or :class:`~django.forms.NullBooleanSelect` if :attr:`null=True <Field.null>`.
The default value of ``BooleanField`` is ``None`` when :attr:`Field.default`
isn't defined.
+``CompositePrimaryKey``
+-----------------------
+
+.. versionadded:: 5.2
+
+.. class:: CompositePrimaryKey(*field_names, **options)
+
+A virtual field used for defining a composite primary key.
+
+This field must be defined as the model's ``pk`` field. If present, Django will
+create the underlying model table with a composite primary key.
+
+The ``*field_names`` argument is a list of positional field names that compose
+the primary key.
+
+See :doc:`/topics/composite-primary-key` for more details.
+
``CharField``
-------------
@@ -1615,6 +1632,8 @@ not an instance of ``UUID``.
hyphens, because PostgreSQL and MariaDB 10.7+ store them in a hyphenated
uuid datatype type.
+.. _relationship-fields:
+
Relationship fields
===================
diff --git a/docs/releases/5.2.txt b/docs/releases/5.2.txt
index 7af0b955f6..4b05fd3279 100644
--- a/docs/releases/5.2.txt
+++ b/docs/releases/5.2.txt
@@ -31,6 +31,25 @@ and only officially support the latest release of each series.
What's new in Django 5.2
========================
+Composite Primary Keys
+----------------------
+
+The new :class:`django.db.models.CompositePrimaryKey` allows tables to be
+created with a primary key consisting of multiple fields.
+
+To use a composite primary key, when creating a model set the ``pk`` field to
+be a ``CompositePrimaryKey``::
+
+ from django.db import models
+
+
+ class Release(models.Model):
+ pk = models.CompositePrimaryKey("version", "name")
+ version = models.IntegerField()
+ name = models.CharField(max_length=20)
+
+See :doc:`/topics/composite-primary-key` for more details.
+
Minor features
--------------
diff --git a/docs/topics/composite-primary-key.txt b/docs/topics/composite-primary-key.txt
new file mode 100644
index 0000000000..9e5234ca9f
--- /dev/null
+++ b/docs/topics/composite-primary-key.txt
@@ -0,0 +1,183 @@
+======================
+Composite primary keys
+======================
+
+.. versionadded:: 5.2
+
+In Django, each model has a primary key. By default, this primary key consists
+of a single field.
+
+In most cases, a single primary key should suffice. In database design,
+however, defining a primary key consisting of multiple fields is sometimes
+necessary.
+
+To use a composite primary key, when creating a model set the ``pk`` field to
+be a :class:`.CompositePrimaryKey`::
+
+ class Product(models.Model):
+ name = models.CharField(max_length=100)
+
+
+ class Order(models.Model):
+ reference = models.CharField(max_length=20, primary_key=True)
+
+
+ class OrderLineItem(models.Model):
+ pk = models.CompositePrimaryKey("product_id", "order_id")
+ product = models.ForeignKey(Product, on_delete=models.CASCADE)
+ order = models.ForeignKey(Order, on_delete=models.CASCADE)
+ quantity = models.IntegerField()
+
+This will instruct Django to create a composite primary key
+(``PRIMARY KEY (product_id, order_id)``) when creating the table.
+
+A composite primary key is represented by a ``tuple``:
+
+.. code-block:: pycon
+
+ >>> product = Product.objects.create(name="apple")
+ >>> order = Order.objects.create(reference="A755H")
+ >>> item = OrderLineItem.objects.create(product=product, order=order, quantity=1)
+ >>> item.pk
+ (1, "A755H")
+
+You can assign a ``tuple`` to a composite primary key. This sets the associated
+field values.
+
+.. code-block:: pycon
+
+ >>> item = OrderLineItem(pk=(2, "B142C"))
+ >>> item.pk
+ (2, "B142C")
+ >>> item.product_id
+ 2
+ >>> item.order_id
+ "B142C"
+
+A composite primary key can also be filtered by a ``tuple``:
+
+.. code-block:: pycon
+
+ >>> OrderLineItem.objects.filter(pk=(1, "A755H")).count()
+ 1
+
+We're still working on composite primary key support for
+:ref:`relational fields <cpk-and-relations>`, including
+:class:`.GenericForeignKey` fields, and the Django admin. Models with composite
+primary keys cannot be registered in the Django admin at this time. You can
+expect to see this in future releases.
+
+Migrating to a composite primary key
+====================================
+
+Django doesn't support migrating to, or from, a composite primary key after the
+table is created. It also doesn't support adding or removing fields from the
+composite primary key.
+
+If you would like to migrate an existing table from a single primary key to a
+composite primary key, follow your database backend's instructions to do so.
+
+Once the composite primary key is in place, add the ``CompositePrimaryKey``
+field to your model. This allows Django to recognize and handle the composite
+primary key appropriately.
+
+While migration operations (e.g. ``AddField``, ``AlterField``) on primary key
+fields are not supported, ``makemigrations`` will still detect changes.
+
+In order to avoid errors, it's recommended to apply such migrations with
+``--fake``.
+
+Alternatively, :class:`.SeparateDatabaseAndState` may be used to execute the
+backend-specific migrations and Django-generated migrations in a single
+operation.
+
+.. _cpk-and-relations:
+
+Composite primary keys and relations
+====================================
+
+:ref:`Relationship fields <relationship-fields>`, including
+:ref:`generic relations <generic-relations>` do not support composite primary
+keys.
+
+For example, given the ``OrderLineItem`` model, the following is not
+supported::
+
+ class Foo(models.Model):
+ item = models.ForeignKey(OrderLineItem, on_delete=models.CASCADE)
+
+Because ``ForeignKey`` currently cannot reference models with composite primary
+keys.
+
+To work around this limitation, ``ForeignObject`` can be used as an
+alternative::
+
+ class Foo(models.Model):
+ item_order_id = models.IntegerField()
+ item_product_id = models.CharField(max_length=20)
+ item = models.ForeignObject(
+ OrderLineItem,
+ on_delete=models.CASCADE,
+ from_fields=("item_order_id", "item_product_id"),
+ to_fields=("order_id", "product_id"),
+ )
+
+``ForeignObject`` is much like ``ForeignKey``, except that it doesn't create
+any columns (e.g. ``item_id``), foreign key constraints or indexes in the
+database.
+
+.. warning::
+
+ ``ForeignObject`` is an internal API. This means it is not covered by our
+ :ref:`deprecation policy <internal-release-deprecation-policy>`.
+
+Composite primary keys and database functions
+=============================================
+
+Many database functions only accept a single expression.
+
+.. code-block:: sql
+
+ MAX("order_id") -- OK
+ MAX("product_id", "order_id") -- ERROR
+
+As a consequence, they cannot be used with composite primary key references as
+they are composed of multiple column expressions.
+
+.. code-block:: python
+
+ Max("order_id") # OK
+ Max("pk") # ERROR
+
+Composite primary keys in forms
+===============================
+
+As a composite primary key is a virtual field, a field which doesn't represent
+a single database column, this field is excluded from ModelForms.
+
+For example, take the following form::
+
+ class OrderLineItemForm(forms.ModelForm):
+ class Meta:
+ model = OrderLineItem
+ fields = "__all__"
+
+This form does not have a form field ``pk`` for the composite primary key:
+
+.. code-block:: pycon
+
+ >>> OrderLineItemForm()
+ <OrderLineItemForm bound=False, valid=Unknown, fields=(product;order;quantity)>
+
+Setting the primary composite field ``pk`` as a form field raises an unknown
+field :exc:`.FieldError`.
+
+.. admonition:: Primary key fields are read only
+
+ If you change the value of a primary key on an existing object and then
+ save it, a new object will be created alongside the old one (see
+ :attr:`.Field.primary_key`).
+
+ This is also true of composite primary keys. Hence, you may want to set
+ :attr:`.Field.editable` to ``False`` on all primary key fields to exclude
+ them from ModelForms.
diff --git a/docs/topics/index.txt b/docs/topics/index.txt
index ffb9fa9d92..4f837c81e2 100644
--- a/docs/topics/index.txt
+++ b/docs/topics/index.txt
@@ -19,6 +19,7 @@ Introductions to all the key parts of Django you'll need to know:
auth/index
cache
conditional-view-processing
+ composite-primary-key
signing
email
i18n/index