Primary Keys, Auto Fields, And Uuids
Understand AutoField, BigAutoField, and how Django picks a default primary key. Learn when to use UUIDField as a primary key, how to generate values, and the trade offs vs integer keys.
1. Introduction
Every Django model needs a primary key — a unique identifier for each row in the database. By default, Django adds one automatically. But there are cases where you need to change the type or use a completely different approach like UUIDs.
This guide covers how Django handles primary keys by default, when to switch to BigAutoField, and when and how to use UUIDField as a primary key.
- You should already have a working model in
pages/models.py. - Your
.venvmust be active and Django 5.2 installed.
2. The default primary key
When you define a model without a primary key field, Django adds one automatically:
class Article(models.Model):
title = models.CharField(max_length=200)
# Django adds this automatically:
# id = models.BigAutoField(primary_key=True)
The auto-generated field is called id and is a BigAutoField — a 64-bit auto-incrementing integer. Django 3.2 changed the default from AutoField (32-bit) to BigAutoField (64-bit). You can confirm this in your settings:
# mysite/settings/base.py
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
This setting controls the default primary key type for all models in the project that do not define their own. Keep it as BigAutoField — it handles far more rows than you will ever need and avoids the overflow issues that older AutoField (32-bit, max ~2 billion rows) could cause in high-traffic tables.
3. AutoField vs BigAutoField
AutoField— 32-bit integer. Supports up to ~2.1 billion rows. Used to be the default before Django 3.2.BigAutoField— 64-bit integer. Supports up to ~9.2 quintillion rows. The current default.SmallAutoField— 16-bit integer. Supports up to ~32,000 rows. Only use for very small lookup tables.
For new projects, always use BigAutoField. If you are working on an older project that still uses AutoField as default, update DEFAULT_AUTO_FIELD in settings. This only affects new tables — existing ones are not changed.
4. Defining a custom primary key
You can define your own primary key by setting primary_key=True on any field. When you do this, Django does not add the automatic id field.
class Country(models.Model):
code = models.CharField(max_length=2, primary_key=True)
name = models.CharField(max_length=100)
def __str__(self):
return self.name
In this example code is the primary key — for example 'US', 'GB', 'DE'. Use natural keys like this only when the value is truly unique, stable, and will never change. If there is any chance the value changes, use an auto-generated integer or UUID instead.
5. UUID primary keys
A UUID (Universally Unique Identifier) is a 128-bit value that looks like 550e8400-e29b-41d4-a716-446655440000. Using a UUID as a primary key has specific advantages over auto-incrementing integers:
- No sequential IDs in URLs — integer IDs like
/articles/1/,/articles/2/reveal how many records you have and make scraping easy. UUIDs do not. - Safe to generate on the client — you can generate a UUID before inserting the record, which is useful in distributed systems.
- No collision risk when merging databases — two separate databases can never generate the same UUID by accident.
Setting up a UUID primary key
import uuid
from django.db import models
class Article(models.Model):
id = models.UUIDField(
primary_key=True,
default=uuid.uuid4,
editable=False,
)
title = models.CharField(max_length=200)
content = models.TextField()
created = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.title
default=uuid.uuid4— passes the function without calling it, so Django generates a fresh UUID for each new record.editable=False— hides the field from forms and the admin so it cannot be changed accidentally.primary_key=True— replaces the default auto-generatedidfield.
6. Using UUIDs in URLs
Django's URL routing includes a built-in uuid path converter that validates and converts UUID values automatically:
# pages/urls.py
from django.urls import path
from . import views
urlpatterns = [
path('articles/<uuid:pk>/', views.article_detail, name='article-detail'),
]
The <uuid:pk> converter only matches valid UUID strings and converts them to a Python UUID object automatically. Invalid UUID strings in the URL return a 404 — no extra validation needed in the view.
7. Integer IDs vs UUIDs — which to choose
- Use integer IDs when simplicity matters, the project is internal, or you need maximum database performance. Integer primary keys are faster to index and join than UUIDs in most databases.
- Use UUIDs when records are exposed in public URLs, you need to merge data from multiple sources, or security requires that IDs are not guessable.
Category or Tag and UUID primary keys for user-facing models like Article or Order where the ID appears in URLs.
8. Next steps
You now understand how primary keys work and when to use each type. The next step covers relationships — how to link models together using ForeignKey, OneToOneField, and ManyToManyField.