{"title":"Composite primary keys","version":"5.2","locale":"el","docname":"topics/composite-primary-key","url":"/el/5.2/topics/composite-primary-key/","canonical":"https://djangodocs.dev/el/5.2/topics/composite-primary-key/","summary":"New in Django 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…","html":"<h1>Composite primary keys<a class=\"heading-anchor\" href=\"#composite-primary-keys\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h1>\n<aside class=\"version-note version-added\" data-version=\"5.2\">\n<p class=\"version-note-title\">New in Django 5.2</p></aside>\n<p>In Django, each model has a primary key. By default, this primary key consists\nof a single field.</p>\n<p>In most cases, a single primary key should suffice. In database design,\nhowever, defining a primary key consisting of multiple fields is sometimes\nnecessary.</p>\n<p>To use a composite primary key, when defining a model set the <code class=\"docutils literal notranslate\"><span class=\"pre\">pk</span></code> attribute\nto be a <a class=\"reference internal\" href=\"/el/5.2/ref/models/fields/#django.db.models.CompositePrimaryKey\" title=\"django.db.models.CompositePrimaryKey\"><code class=\"xref py py-class docutils literal notranslate\"><span class=\"pre\">CompositePrimaryKey</span></code></a>:</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"k\">class</span><span class=\"w\"> </span><span class=\"nc\">Product</span><span class=\"p\">(</span><span class=\"n\">models</span><span class=\"o\">.</span><span class=\"n\">Model</span><span class=\"p\">):</span>\n    <span class=\"n\">name</span> <span class=\"o\">=</span> <span class=\"n\">models</span><span class=\"o\">.</span><span class=\"n\">CharField</span><span class=\"p\">(</span><span class=\"n\">max_length</span><span class=\"o\">=</span><span class=\"mi\">100</span><span class=\"p\">)</span>\n\n\n<span class=\"k\">class</span><span class=\"w\"> </span><span class=\"nc\">Order</span><span class=\"p\">(</span><span class=\"n\">models</span><span class=\"o\">.</span><span class=\"n\">Model</span><span class=\"p\">):</span>\n    <span class=\"n\">reference</span> <span class=\"o\">=</span> <span class=\"n\">models</span><span class=\"o\">.</span><span class=\"n\">CharField</span><span class=\"p\">(</span><span class=\"n\">max_length</span><span class=\"o\">=</span><span class=\"mi\">20</span><span class=\"p\">,</span> <span class=\"n\">primary_key</span><span class=\"o\">=</span><span class=\"kc\">True</span><span class=\"p\">)</span>\n\n\n<span class=\"k\">class</span><span class=\"w\"> </span><span class=\"nc\">OrderLineItem</span><span class=\"p\">(</span><span class=\"n\">models</span><span class=\"o\">.</span><span class=\"n\">Model</span><span class=\"p\">):</span>\n    <span class=\"n\">pk</span> <span class=\"o\">=</span> <span class=\"n\">models</span><span class=\"o\">.</span><span class=\"n\">CompositePrimaryKey</span><span class=\"p\">(</span><span class=\"s2\">&quot;product_id&quot;</span><span class=\"p\">,</span> <span class=\"s2\">&quot;order_id&quot;</span><span class=\"p\">)</span>\n    <span class=\"n\">product</span> <span class=\"o\">=</span> <span class=\"n\">models</span><span class=\"o\">.</span><span class=\"n\">ForeignKey</span><span class=\"p\">(</span><span class=\"n\">Product</span><span class=\"p\">,</span> <span class=\"n\">on_delete</span><span class=\"o\">=</span><span class=\"n\">models</span><span class=\"o\">.</span><span class=\"n\">CASCADE</span><span class=\"p\">)</span>\n    <span class=\"n\">order</span> <span class=\"o\">=</span> <span class=\"n\">models</span><span class=\"o\">.</span><span class=\"n\">ForeignKey</span><span class=\"p\">(</span><span class=\"n\">Order</span><span class=\"p\">,</span> <span class=\"n\">on_delete</span><span class=\"o\">=</span><span class=\"n\">models</span><span class=\"o\">.</span><span class=\"n\">CASCADE</span><span class=\"p\">)</span>\n    <span class=\"n\">quantity</span> <span class=\"o\">=</span> <span class=\"n\">models</span><span class=\"o\">.</span><span class=\"n\">IntegerField</span><span class=\"p\">()</span>\n</code></pre></div>\n<p>This will instruct Django to create a composite primary key\n(<code class=\"docutils literal notranslate\"><span class=\"pre\">PRIMARY</span> <span class=\"pre\">KEY</span> <span class=\"pre\">(product_id,</span> <span class=\"pre\">order_id)</span></code>) when creating the table.</p>\n<p>A composite primary key is represented by a <code class=\"docutils literal notranslate\"><span class=\"pre\">tuple</span></code>:</p>\n<div class=\"code-block\" data-language=\"pycon\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python console</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Python console code\"><code><span class=\"gp\">&gt;&gt;&gt; </span><span class=\"n\">product</span> <span class=\"o\">=</span> <span class=\"n\">Product</span><span class=\"o\">.</span><span class=\"n\">objects</span><span class=\"o\">.</span><span class=\"n\">create</span><span class=\"p\">(</span><span class=\"n\">name</span><span class=\"o\">=</span><span class=\"s2\">&quot;apple&quot;</span><span class=\"p\">)</span>\n<span class=\"gp\">&gt;&gt;&gt; </span><span class=\"n\">order</span> <span class=\"o\">=</span> <span class=\"n\">Order</span><span class=\"o\">.</span><span class=\"n\">objects</span><span class=\"o\">.</span><span class=\"n\">create</span><span class=\"p\">(</span><span class=\"n\">reference</span><span class=\"o\">=</span><span class=\"s2\">&quot;A755H&quot;</span><span class=\"p\">)</span>\n<span class=\"gp\">&gt;&gt;&gt; </span><span class=\"n\">item</span> <span class=\"o\">=</span> <span class=\"n\">OrderLineItem</span><span class=\"o\">.</span><span class=\"n\">objects</span><span class=\"o\">.</span><span class=\"n\">create</span><span class=\"p\">(</span><span class=\"n\">product</span><span class=\"o\">=</span><span class=\"n\">product</span><span class=\"p\">,</span> <span class=\"n\">order</span><span class=\"o\">=</span><span class=\"n\">order</span><span class=\"p\">,</span> <span class=\"n\">quantity</span><span class=\"o\">=</span><span class=\"mi\">1</span><span class=\"p\">)</span>\n<span class=\"gp\">&gt;&gt;&gt; </span><span class=\"n\">item</span><span class=\"o\">.</span><span class=\"n\">pk</span>\n<span class=\"go\">(1, &quot;A755H&quot;)</span>\n</code></pre></div>\n<p>You can assign a <code class=\"docutils literal notranslate\"><span class=\"pre\">tuple</span></code> to the <a class=\"reference internal\" href=\"/el/5.2/ref/models/instances/#django.db.models.Model.pk\" title=\"django.db.models.Model.pk\"><code class=\"xref py py-attr docutils literal notranslate\"><span class=\"pre\">pk</span></code></a> attribute.\nThis sets the associated field values:</p>\n<div class=\"code-block\" data-language=\"pycon\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python console</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Python console code\"><code><span class=\"gp\">&gt;&gt;&gt; </span><span class=\"n\">item</span> <span class=\"o\">=</span> <span class=\"n\">OrderLineItem</span><span class=\"p\">(</span><span class=\"n\">pk</span><span class=\"o\">=</span><span class=\"p\">(</span><span class=\"mi\">2</span><span class=\"p\">,</span> <span class=\"s2\">&quot;B142C&quot;</span><span class=\"p\">))</span>\n<span class=\"gp\">&gt;&gt;&gt; </span><span class=\"n\">item</span><span class=\"o\">.</span><span class=\"n\">pk</span>\n<span class=\"go\">(2, &quot;B142C&quot;)</span>\n<span class=\"gp\">&gt;&gt;&gt; </span><span class=\"n\">item</span><span class=\"o\">.</span><span class=\"n\">product_id</span>\n<span class=\"go\">2</span>\n<span class=\"gp\">&gt;&gt;&gt; </span><span class=\"n\">item</span><span class=\"o\">.</span><span class=\"n\">order_id</span>\n<span class=\"go\">&quot;B142C&quot;</span>\n</code></pre></div>\n<p>A composite primary key can also be filtered by a <code class=\"docutils literal notranslate\"><span class=\"pre\">tuple</span></code>:</p>\n<div class=\"code-block\" data-language=\"pycon\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python console</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Python console code\"><code><span class=\"gp\">&gt;&gt;&gt; </span><span class=\"n\">OrderLineItem</span><span class=\"o\">.</span><span class=\"n\">objects</span><span class=\"o\">.</span><span class=\"n\">filter</span><span class=\"p\">(</span><span class=\"n\">pk</span><span class=\"o\">=</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">,</span> <span class=\"s2\">&quot;A755H&quot;</span><span class=\"p\">))</span><span class=\"o\">.</span><span class=\"n\">count</span><span class=\"p\">()</span>\n<span class=\"go\">1</span>\n</code></pre></div>\n<p>We’re still working on composite primary key support for\n<a class=\"reference internal\" href=\"#cpk-and-relations\"><span class=\"std std-ref\">relational fields</span></a>, including\n<a class=\"reference internal\" href=\"/el/5.2/ref/contrib/contenttypes/#django.contrib.contenttypes.fields.GenericForeignKey\" title=\"django.contrib.contenttypes.fields.GenericForeignKey\"><code class=\"xref py py-class docutils literal notranslate\"><span class=\"pre\">GenericForeignKey</span></code></a> fields, and the Django admin. Models with composite\nprimary keys cannot be registered in the Django admin at this time. You can\nexpect to see this in future releases.</p>\n<section id=\"migrating-to-a-composite-primary-key\">\n<h2>Migrating to a composite primary key<a class=\"heading-anchor\" href=\"#migrating-to-a-composite-primary-key\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>Django doesn’t support migrating to, or from, a composite primary key after the\ntable is created. It also doesn’t support adding or removing fields from the\ncomposite primary key.</p>\n<p>If you would like to migrate an existing table from a single primary key to a\ncomposite primary key, follow your database backend’s instructions to do so.</p>\n<p>Once the composite primary key is in place, add the <code class=\"docutils literal notranslate\"><span class=\"pre\">CompositePrimaryKey</span></code>\nfield to your model. This allows Django to recognize and handle the composite\nprimary key appropriately.</p>\n<p>While migration operations (e.g. <code class=\"docutils literal notranslate\"><span class=\"pre\">AddField</span></code>, <code class=\"docutils literal notranslate\"><span class=\"pre\">AlterField</span></code>) on primary key\nfields are not supported, <code class=\"docutils literal notranslate\"><span class=\"pre\">makemigrations</span></code> will still detect changes.</p>\n<p>In order to avoid errors, it’s recommended to apply such migrations with\n<code class=\"docutils literal notranslate\"><span class=\"pre\">--fake</span></code>.</p>\n<p>Alternatively, <a class=\"reference internal\" href=\"/el/5.2/ref/migration-operations/#django.db.migrations.operations.SeparateDatabaseAndState\" title=\"django.db.migrations.operations.SeparateDatabaseAndState\"><code class=\"xref py py-class docutils literal notranslate\"><span class=\"pre\">SeparateDatabaseAndState</span></code></a> may be used to execute the\nbackend-specific migrations and Django-generated migrations in a single\noperation.</p>\n</section>\n<section id=\"composite-primary-keys-and-relations\">\n<span id=\"cpk-and-relations\"></span><h2>Composite primary keys and relations<a class=\"heading-anchor\" href=\"#composite-primary-keys-and-relations\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p><a class=\"reference internal\" href=\"/el/5.2/ref/models/fields/#relationship-fields\"><span class=\"std std-ref\">Relationship fields</span></a>, including\n<a class=\"reference internal\" href=\"/el/5.2/ref/contrib/contenttypes/#generic-relations\"><span class=\"std std-ref\">generic relations</span></a> do not support composite primary\nkeys.</p>\n<p>For example, given the <code class=\"docutils literal notranslate\"><span class=\"pre\">OrderLineItem</span></code> model, the following is not\nsupported:</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"k\">class</span><span class=\"w\"> </span><span class=\"nc\">Foo</span><span class=\"p\">(</span><span class=\"n\">models</span><span class=\"o\">.</span><span class=\"n\">Model</span><span class=\"p\">):</span>\n    <span class=\"n\">item</span> <span class=\"o\">=</span> <span class=\"n\">models</span><span class=\"o\">.</span><span class=\"n\">ForeignKey</span><span class=\"p\">(</span><span class=\"n\">OrderLineItem</span><span class=\"p\">,</span> <span class=\"n\">on_delete</span><span class=\"o\">=</span><span class=\"n\">models</span><span class=\"o\">.</span><span class=\"n\">CASCADE</span><span class=\"p\">)</span>\n</code></pre></div>\n<p>Because <code class=\"docutils literal notranslate\"><span class=\"pre\">ForeignKey</span></code> currently cannot reference models with composite primary\nkeys.</p>\n<p>To work around this limitation, <code class=\"docutils literal notranslate\"><span class=\"pre\">ForeignObject</span></code> can be used as an\nalternative:</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"k\">class</span><span class=\"w\"> </span><span class=\"nc\">Foo</span><span class=\"p\">(</span><span class=\"n\">models</span><span class=\"o\">.</span><span class=\"n\">Model</span><span class=\"p\">):</span>\n    <span class=\"n\">item_order_id</span> <span class=\"o\">=</span> <span class=\"n\">models</span><span class=\"o\">.</span><span class=\"n\">CharField</span><span class=\"p\">(</span><span class=\"n\">max_length</span><span class=\"o\">=</span><span class=\"mi\">20</span><span class=\"p\">)</span>\n    <span class=\"n\">item_product_id</span> <span class=\"o\">=</span> <span class=\"n\">models</span><span class=\"o\">.</span><span class=\"n\">IntegerField</span><span class=\"p\">()</span>\n    <span class=\"n\">item</span> <span class=\"o\">=</span> <span class=\"n\">models</span><span class=\"o\">.</span><span class=\"n\">ForeignObject</span><span class=\"p\">(</span>\n        <span class=\"n\">OrderLineItem</span><span class=\"p\">,</span>\n        <span class=\"n\">on_delete</span><span class=\"o\">=</span><span class=\"n\">models</span><span class=\"o\">.</span><span class=\"n\">CASCADE</span><span class=\"p\">,</span>\n        <span class=\"n\">from_fields</span><span class=\"o\">=</span><span class=\"p\">(</span><span class=\"s2\">&quot;item_order_id&quot;</span><span class=\"p\">,</span> <span class=\"s2\">&quot;item_product_id&quot;</span><span class=\"p\">),</span>\n        <span class=\"n\">to_fields</span><span class=\"o\">=</span><span class=\"p\">(</span><span class=\"s2\">&quot;order_id&quot;</span><span class=\"p\">,</span> <span class=\"s2\">&quot;product_id&quot;</span><span class=\"p\">),</span>\n    <span class=\"p\">)</span>\n</code></pre></div>\n<p><code class=\"docutils literal notranslate\"><span class=\"pre\">ForeignObject</span></code> is much like <code class=\"docutils literal notranslate\"><span class=\"pre\">ForeignKey</span></code>, except that it doesn’t create\nany columns (e.g. <code class=\"docutils literal notranslate\"><span class=\"pre\">item_id</span></code>), foreign key constraints or indexes in the\ndatabase, and the <code class=\"docutils literal notranslate\"><span class=\"pre\">on_delete</span></code> argument is ignored.</p>\n<aside class=\"admonition admonition-warning\" role=\"note\">\n<p class=\"admonition-title\">Προειδοποίηση</p>\n<p><code class=\"docutils literal notranslate\"><span class=\"pre\">ForeignObject</span></code> is an internal API. This means it is not covered by our\n<a class=\"reference internal\" href=\"/el/5.2/internals/release-process/#internal-release-deprecation-policy\"><span class=\"std std-ref\">deprecation policy</span></a>.</p>\n</aside>\n</section>\n<section id=\"composite-primary-keys-and-database-functions\">\n<span id=\"cpk-and-database-functions\"></span><h2>Composite primary keys and database functions<a class=\"heading-anchor\" href=\"#composite-primary-keys-and-database-functions\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>Many database functions only accept a single expression.</p>\n<div class=\"code-block\" data-language=\"sql\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">SQL</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"SQL code\"><code><span class=\"k\">MAX</span><span class=\"p\">(</span><span class=\"ss\">&quot;order_id&quot;</span><span class=\"p\">)</span><span class=\"w\">  </span><span class=\"c1\">-- OK</span>\n<span class=\"k\">MAX</span><span class=\"p\">(</span><span class=\"ss\">&quot;product_id&quot;</span><span class=\"p\">,</span><span class=\"w\"> </span><span class=\"ss\">&quot;order_id&quot;</span><span class=\"p\">)</span><span class=\"w\">  </span><span class=\"c1\">-- ERROR</span>\n</code></pre></div>\n<p>In these cases, providing a composite primary key reference raises a\n<code class=\"docutils literal notranslate\"><span class=\"pre\">ValueError</span></code>, since it is composed of multiple column expressions. An\nexception is made for <code class=\"docutils literal notranslate\"><span class=\"pre\">Count</span></code>.</p>\n<div class=\"code-block\" data-language=\"python\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Python code\"><code><span class=\"n\">Max</span><span class=\"p\">(</span><span class=\"s2\">&quot;order_id&quot;</span><span class=\"p\">)</span>  <span class=\"c1\"># OK</span>\n<span class=\"n\">Max</span><span class=\"p\">(</span><span class=\"s2\">&quot;pk&quot;</span><span class=\"p\">)</span>  <span class=\"c1\"># ValueError</span>\n<span class=\"n\">Count</span><span class=\"p\">(</span><span class=\"s2\">&quot;pk&quot;</span><span class=\"p\">)</span>  <span class=\"c1\"># OK</span>\n</code></pre></div>\n</section>\n<section id=\"composite-primary-keys-in-forms\">\n<h2>Composite primary keys in forms<a class=\"heading-anchor\" href=\"#composite-primary-keys-in-forms\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>As a composite primary key is a virtual field, a field which doesn’t represent\na single database column, this field is excluded from ModelForms.</p>\n<p>For example, take the following form:</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"k\">class</span><span class=\"w\"> </span><span class=\"nc\">OrderLineItemForm</span><span class=\"p\">(</span><span class=\"n\">forms</span><span class=\"o\">.</span><span class=\"n\">ModelForm</span><span class=\"p\">):</span>\n    <span class=\"k\">class</span><span class=\"w\"> </span><span class=\"nc\">Meta</span><span class=\"p\">:</span>\n        <span class=\"n\">model</span> <span class=\"o\">=</span> <span class=\"n\">OrderLineItem</span>\n        <span class=\"n\">fields</span> <span class=\"o\">=</span> <span class=\"s2\">&quot;__all__&quot;</span>\n</code></pre></div>\n<p>This form does not have a form field <code class=\"docutils literal notranslate\"><span class=\"pre\">pk</span></code> for the composite primary key:</p>\n<div class=\"code-block\" data-language=\"pycon\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python console</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Python console code\"><code><span class=\"gp\">&gt;&gt;&gt; </span><span class=\"n\">OrderLineItemForm</span><span class=\"p\">()</span>\n<span class=\"go\">&lt;OrderLineItemForm bound=False, valid=Unknown, fields=(product;order;quantity)&gt;</span>\n</code></pre></div>\n<p>Setting the primary composite field <code class=\"docutils literal notranslate\"><span class=\"pre\">pk</span></code> as a form field raises an unknown\nfield <a class=\"reference internal\" href=\"/el/5.2/ref/exceptions/#django.core.exceptions.FieldError\" title=\"django.core.exceptions.FieldError\"><code class=\"xref py py-exc docutils literal notranslate\"><span class=\"pre\">FieldError</span></code></a>.</p>\n<aside class=\"admonition-primary-key-fields-are-read-only admonition\">\n<p class=\"admonition-title\">Primary key fields are read only</p>\n<p>If you change the value of a primary key on an existing object and then\nsave it, a new object will be created alongside the old one (see\n<a class=\"reference internal\" href=\"/el/5.2/ref/models/fields/#django.db.models.Field.primary_key\" title=\"django.db.models.Field.primary_key\"><code class=\"xref py py-attr docutils literal notranslate\"><span class=\"pre\">Field.primary_key</span></code></a>).</p>\n<p>This is also true of composite primary keys. Hence, you may want to set\n<a class=\"reference internal\" href=\"/el/5.2/ref/models/fields/#django.db.models.Field.editable\" title=\"django.db.models.Field.editable\"><code class=\"xref py py-attr docutils literal notranslate\"><span class=\"pre\">Field.editable</span></code></a> to <code class=\"docutils literal notranslate\"><span class=\"pre\">False</span></code> on all primary key fields to exclude\nthem from ModelForms.</p>\n</aside>\n</section>\n<section id=\"composite-primary-keys-in-model-validation\">\n<h2>Composite primary keys in model validation<a class=\"heading-anchor\" href=\"#composite-primary-keys-in-model-validation\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>Since <code class=\"docutils literal notranslate\"><span class=\"pre\">pk</span></code> is only a virtual field, including <code class=\"docutils literal notranslate\"><span class=\"pre\">pk</span></code> as a field name in the\n<code class=\"docutils literal notranslate\"><span class=\"pre\">exclude</span></code> argument of <a class=\"reference internal\" href=\"/el/5.2/ref/models/instances/#django.db.models.Model.clean_fields\" title=\"django.db.models.Model.clean_fields\"><code class=\"xref py py-meth docutils literal notranslate\"><span class=\"pre\">Model.clean_fields()</span></code></a> has no effect. To exclude\nthe composite primary key fields from\n<a class=\"reference internal\" href=\"/el/5.2/ref/models/instances/#validating-objects\"><span class=\"std std-ref\">model validation</span></a>, specify each field individually.\n<a class=\"reference internal\" href=\"/el/5.2/ref/models/instances/#django.db.models.Model.validate_unique\" title=\"django.db.models.Model.validate_unique\"><code class=\"xref py py-meth docutils literal notranslate\"><span class=\"pre\">Model.validate_unique()</span></code></a> can still be called with <code class=\"docutils literal notranslate\"><span class=\"pre\">exclude={&quot;pk&quot;}</span></code> to\nskip uniqueness checks.</p>\n</section>\n<section id=\"building-composite-primary-key-ready-applications\">\n<h2>Building composite primary key ready applications<a class=\"heading-anchor\" href=\"#building-composite-primary-key-ready-applications\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>Prior to the introduction of composite primary keys, the single field composing\nthe primary key of a model could be retrieved by introspecting the\n<a class=\"reference internal\" href=\"/el/5.2/ref/models/fields/#django.db.models.Field.primary_key\" title=\"django.db.models.Field.primary_key\"><code class=\"xref py py-attr docutils literal notranslate\"><span class=\"pre\">primary</span> <span class=\"pre\">key</span></code></a> attribute of its\nfields:</p>\n<div class=\"code-block\" data-language=\"pycon\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python console</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Python console code\"><code><span class=\"gp\">&gt;&gt;&gt; </span><span class=\"n\">pk_field</span> <span class=\"o\">=</span> <span class=\"kc\">None</span>\n<span class=\"gp\">&gt;&gt;&gt; </span><span class=\"k\">for</span> <span class=\"n\">field</span> <span class=\"ow\">in</span> <span class=\"n\">Product</span><span class=\"o\">.</span><span class=\"n\">_meta</span><span class=\"o\">.</span><span class=\"n\">get_fields</span><span class=\"p\">():</span>\n<span class=\"gp\">... </span>    <span class=\"k\">if</span> <span class=\"n\">field</span><span class=\"o\">.</span><span class=\"n\">primary_key</span><span class=\"p\">:</span>\n<span class=\"gp\">... </span>        <span class=\"n\">pk_field</span> <span class=\"o\">=</span> <span class=\"n\">field</span>\n<span class=\"gp\">... </span>        <span class=\"k\">break</span>\n<span class=\"gp\">...</span>\n<span class=\"gp\">&gt;&gt;&gt; </span><span class=\"n\">pk_field</span>\n<span class=\"go\">&lt;django.db.models.fields.AutoField: id&gt;</span>\n</code></pre></div>\n<p>Now that a primary key can be composed of multiple fields the\n<a class=\"reference internal\" href=\"/el/5.2/ref/models/fields/#django.db.models.Field.primary_key\" title=\"django.db.models.Field.primary_key\"><code class=\"xref py py-attr docutils literal notranslate\"><span class=\"pre\">primary</span> <span class=\"pre\">key</span></code></a> attribute can no\nlonger be relied upon to identify members of the primary key as it will be set\nto <code class=\"docutils literal notranslate\"><span class=\"pre\">False</span></code> to maintain the invariant that at most one field per model will\nhave this attribute set to <code class=\"docutils literal notranslate\"><span class=\"pre\">True</span></code>:</p>\n<div class=\"code-block\" data-language=\"pycon\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python console</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Python console code\"><code><span class=\"gp\">&gt;&gt;&gt; </span><span class=\"n\">pk_fields</span> <span class=\"o\">=</span> <span class=\"p\">[]</span>\n<span class=\"gp\">&gt;&gt;&gt; </span><span class=\"k\">for</span> <span class=\"n\">field</span> <span class=\"ow\">in</span> <span class=\"n\">OrderLineItem</span><span class=\"o\">.</span><span class=\"n\">_meta</span><span class=\"o\">.</span><span class=\"n\">get_fields</span><span class=\"p\">():</span>\n<span class=\"gp\">... </span>    <span class=\"k\">if</span> <span class=\"n\">field</span><span class=\"o\">.</span><span class=\"n\">primary_key</span><span class=\"p\">:</span>\n<span class=\"gp\">... </span>        <span class=\"n\">pk_fields</span><span class=\"o\">.</span><span class=\"n\">append</span><span class=\"p\">(</span><span class=\"n\">field</span><span class=\"p\">)</span>\n<span class=\"gp\">...</span>\n<span class=\"gp\">&gt;&gt;&gt; </span><span class=\"n\">pk_fields</span>\n<span class=\"go\">[]</span>\n</code></pre></div>\n<p>In order to build application code that properly handles composite primary\nkeys the <a class=\"reference internal\" href=\"/el/5.2/ref/models/meta/#django.db.models.options.Options.pk_fields\" title=\"django.db.models.options.Options.pk_fields\"><code class=\"xref py py-attr docutils literal notranslate\"><span class=\"pre\">_meta.pk_fields</span></code></a>\nattribute should be used instead:</p>\n<div class=\"code-block\" data-language=\"pycon\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python console</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Python console code\"><code><span class=\"gp\">&gt;&gt;&gt; </span><span class=\"n\">Product</span><span class=\"o\">.</span><span class=\"n\">_meta</span><span class=\"o\">.</span><span class=\"n\">pk_fields</span>\n<span class=\"go\">[&lt;django.db.models.fields.AutoField: id&gt;]</span>\n<span class=\"gp\">&gt;&gt;&gt; </span><span class=\"n\">OrderLineItem</span><span class=\"o\">.</span><span class=\"n\">_meta</span><span class=\"o\">.</span><span class=\"n\">pk_fields</span>\n<span class=\"go\">[</span>\n<span class=\"go\">    &lt;django.db.models.fields.ForeignKey: product&gt;,</span>\n<span class=\"go\">    &lt;django.db.models.fields.ForeignKey: order&gt;</span>\n<span class=\"go\">]</span>\n</code></pre></div>\n</section>","rootId":"composite-primary-keys","toc":[{"title":"Migrating to a composite primary key","anchor":"migrating-to-a-composite-primary-key","children":[]},{"title":"Composite primary keys and relations","anchor":"composite-primary-keys-and-relations","children":[]},{"title":"Composite primary keys and database functions","anchor":"composite-primary-keys-and-database-functions","children":[]},{"title":"Composite primary keys in forms","anchor":"composite-primary-keys-in-forms","children":[]},{"title":"Composite primary keys in model validation","anchor":"composite-primary-keys-in-model-validation","children":[]},{"title":"Building composite primary key ready applications","anchor":"building-composite-primary-key-ready-applications","children":[]}],"breadcrumbs":[{"docname":"topics/index","title":"Using Django","url":"/el/5.2/topics/"}],"prev":{"docname":"topics/conditional-view-processing","title":"Conditional View Processing","url":"/el/5.2/topics/conditional-view-processing/"},"next":{"docname":"topics/signing","title":"Cryptographic signing","url":"/el/5.2/topics/signing/"},"formats":{"html":"/el/5.2/topics/composite-primary-key/","markdown":"/el/5.2/topics/composite-primary-key.md","json":"/el/5.2/topics/composite-primary-key.json"},"source":"https://github.com/django/django/blob/stable/5.2.x/docs/topics/composite-primary-key.txt","official":"https://docs.djangoproject.com/el/5.2/topics/composite-primary-key/","inVersions":["6.1","6.0","5.2"],"inLocales":["en","sv","zh-hans","ga","fr","ja","id","it","pt-br","ko","es","el","pl"]}