skip to content

Generated Columns & Keys

GeneratedField columns computed by the database, CompositePrimaryKey (5.2), BigAutoField as the default key and UUID keys. Interviewers probe primary key choice and its index cost.

part ofDjangooverview, primer and where to startread it →
on this pageshow

explore

questions

4

In Django 6.x, what primary key does a model get when it declares none, and how do DEFAULT_AUTO_FIELD and AppConfig.default_auto_field change it?

level: juniorimportance: must knowfreq 55%

answer

  1. an implicit id column
  2. project setting versus per-app override
  3. the default changed in 6.0
  4. must be an AutoField subclass

basics

~10 s

Django adds an auto-incrementing id field. Its class comes from the app's AppConfig.default_auto_field if set, otherwise from the DEFAULT_AUTO_FIELD setting, which defaults to BigAutoField since Django 6.0 (AutoField before).

solid answer

~40 s

If no field has `primary_key=True`, Django adds `id = <class>(primary_key=True)` for you. The class is resolved per app: `AppConfig.default_auto_field` wins, falling back to the project-wide `DEFAULT_AUTO_FIELD` setting, which defaults to `django.db.models.BigAutoField` since Django 6.0; it was `AutoField` before, and projects that left it unset saw warning `models.W042` from 3.2 to 5.2. The value must name an `AutoField` subclass (`AutoField`, `BigAutoField`, `SmallAutoField` or your own), so a `UUIDField` is rejected; a UUID key is declared explicitly on the model. Reusable apps should set `default_auto_field` so their shipped migrations match every project. An old project upgraded to 6.x without the setting will get migrations turning `id` columns into big integers unless it pins `AutoField`.

code

python · 10 lines
python
# settings.py: pin the old width during a 5.2 -> 6.x upgrade
DEFAULT_AUTO_FIELD = "django.db.models.AutoField"

# orders/apps.py: a reusable or legacy app can decide for itself
from django.apps import AppConfig


class OrdersConfig(AppConfig):
    name = "orders"
    default_auto_field = "django.db.models.BigAutoField"

go deeper

for a junior

Recall that a model without a primary key gets an auto-incrementing id, and that in Django 6.x it is a BigAutoField.

for a middle

Explain the lookup order, AppConfig.default_auto_field before DEFAULT_AUTO_FIELD, and the AutoField-subclass requirement.

for a senior

Handle the 6.0 upgrade: why unpinned projects see id-widening migrations, the M2M through-table gap, and why reusable apps pin the value.

for a principal

Set a project-wide key policy, integer width, where UUIDs are allowed and how legacy apps are pinned, so upgrades never surprise the schema.

## The implicit primary key Every Django model has **exactly one** primary key. If no field is declared with `primary_key=True`, Django adds one when it builds the model class: ```python id = models.BigAutoField(primary_key=True) # added automatically in Django 6.x ``` It is always named **`id`**, it is an auto-incrementing integer column filled by the database, and `instance.pk` is an alias for it. Declaring any field with `primary_key=True` — or a `CompositePrimaryKey` named `pk` — stops Django adding it. ## Which class Django uses The class of that implicit field is resolved per model, from the app the model belongs to: 1. **`AppConfig.default_auto_field`** on the app's configuration class, if the class sets it; 2. otherwise the **`DEFAULT_AUTO_FIELD`** setting; 3. otherwise the setting's default, **`"django.db.models.BigAutoField"`** since Django 6.0. Django imports the dotted path and checks it. An empty value raises `ImproperlyConfigured`, a path that cannot be imported raises `ImproperlyConfigured`, and a class that is not an **`AutoField` subclass** raises `ValueError`. Valid choices are `AutoField` (32-bit), `BigAutoField` (64-bit), `SmallAutoField` (16-bit) or your own subclass of one of them. A UUID key therefore cannot be made the implicit default; it is declared explicitly on each model that wants it. | Where it is set | Scope | Typical use | |---|---|---| | `DEFAULT_AUTO_FIELD` in settings | every app without its own value | the project's house rule | | `AppConfig.default_auto_field` | one app | reusable apps; legacy apps pinned to `AutoField` | | an explicit `primary_key=True` field | one model | UUID keys, natural keys | ## What changed in Django 6.0 From Django 3.2 to 5.2 the setting existed but defaulted to `AutoField` for backwards compatibility. Instead, `startproject` wrote `DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"` into new settings files, `startapp` wrote the same into each `AppConfig`, and projects that set nothing got system check warning **`models.W042`** ("Auto-created primary key used when not defining a primary key type"). In **Django 6.0** the default itself became `BigAutoField`, the template lines were removed, and `models.W042` no longer exists. Consequences when upgrading: - a project that followed the templates or silenced W042 by setting `BigAutoField` sees no change; - a project that never set anything was on `AutoField`; after the upgrade `makemigrations` wants to alter every implicit `id`, and every foreign key pointing at one, to a big integer. If that is not wanted now, add `DEFAULT_AUTO_FIELD = "django.db.models.AutoField"` (or `default_auto_field` on the affected apps); - auto-created many-to-many through tables are a documented gap: changing the setting migrates the related keys but not the through table's own primary key, which needs a manual `RunSQL` step. ## Reusable apps must pin it An app distributed to many projects ships its own migrations. If it relies on the project setting, a project with a different `DEFAULT_AUTO_FIELD` will generate *new* migrations inside the app's package when it runs `makemigrations`, and those can conflict with the app's future releases. The Django docs therefore say reusable apps defining models **must** set `default_auto_field` on their `AppConfig`. ## Checking what a model actually got Guessing from settings is error-prone when apps override them, so ask Django directly: - `Order._meta.pk` returns the primary key field object, and `type(Order._meta.pk).__name__` names its class; - `Order._meta.pk.name` is `"id"` for an implicit key and the declared name otherwise; - `Order._meta.pk.auto_created` is `True` only when Django added the field itself. In CI, `python manage.py makemigrations --check --dry-run` exits with an error when the models imply migrations that have not been written. After an upgrade, that is the quickest way to learn whether the new default would alter any `id` column before anyone runs `makemigrations` for real and commits dozens of unexpected `AlterField` operations. ## Why `BigAutoField` A 32-bit `AutoField` tops out at about 2.1 billion values. That sounds large until a high-volume table — events, line items, audit rows — exhausts it, at which point inserts fail and widening the column on a live table is a painful migration. A 64-bit key costs four extra bytes per key and per foreign key, which is almost always worth it; that trade-off is why the templates recommended it for years before it became the default.

  • Can DEFAULT_AUTO_FIELD be set to 'django.db.models.UUIDField' to give every model a UUID key?
    No. Django requires the class to subclass `AutoField` and raises `ValueError` otherwise, because the implicit key must be a database-generated integer. Models that need UUID keys declare `id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)` explicitly.
  • A project upgraded from Django 5.2 to 6.1 suddenly gets migrations altering every id column; why?
    It never set `DEFAULT_AUTO_FIELD`, so it ran on the old `AutoField` default; 6.0 changed the default to `BigAutoField`, and the autodetector now sees every implicit `id` and every foreign key to it as changed. Pin `DEFAULT_AUTO_FIELD = "django.db.models.AutoField"` to keep the schema, or plan the widening as a deliberate migration.

saying these in an interview costs you the question

  • Django 6.x still defaults DEFAULT_AUTO_FIELD to AutoField.
  • The project setting overrides an AppConfig's default_auto_field.
  • DEFAULT_AUTO_FIELD can name UUIDField to make UUID keys the default.
  • Changing DEFAULT_AUTO_FIELD also migrates auto-created M2M through table keys.
  • Every Django model needs an explicitly declared primary key field.
open as a page

In a Django model, how does GeneratedField compute a line item's total, and what do its expression, output_field and db_persist arguments control?

level: middleimportance: should knowfreq 35%

basics

~20 s

GeneratedField declares a GENERATED ALWAYS column: expression is the database-side formula, output_field sets the column's type, and db_persist chooses a stored column (True) or a virtual one computed on read (False). Django never writes it.

open as a page

When should a Django model use a UUIDField primary key instead of BigAutoField, and what does that choice cost in storage and index performance?

level: seniorimportance: should knowfreq 45%

basics

~20 s

Use a UUID key when ids must be unguessable or created outside the database. It costs a wider key in every index and foreign key, char(32) off PostgreSQL and MariaDB, and random uuid4 values scatter inserts across the index; time-ordered UUIDv7 avoids that.

open as a page

In Django 5.2+, how do you key a line-item table by (order, line_no) with CompositePrimaryKey, and what are its current limits?

level: middleimportance: nice to knowfreq 22%

basics

~20 s

Declare pk = models.CompositePrimaryKey('order_id', 'line_no'); pk becomes a tuple usable in filters. Limits: ForeignKey and generic relations cannot target the model, the admin cannot register it, and Django cannot migrate a table to or from it.

open as a page