The built-in uuid module
Python ships a uuid module in the standard library — no installation required:
import uuid
# UUID v4 — random, 122 bits of cryptographic randomness
id_v4 = uuid.uuid4()
print(id_v4) # 550e8400-e29b-41d4-a716-446655440000
print(type(id_v4)) # <class 'uuid.UUID'>
# Access different representations
print(str(id_v4)) # '550e8400-e29b-41d4-a716-446655440000' (with hyphens)
print(id_v4.hex) # '550e8400e29b41d4a716446655440000' (no hyphens)
print(id_v4.bytes) # b'\x55\x0e...' (16 raw bytes)
print(id_v4.int) # 113059749145936325402354257176981405696 (integer)
The uuid.UUID object is not a plain string — it is a rich type with multiple representations. Always convert to string explicitly when you need to store or transmit it.
UUID v7 in Python
Python 3.13+
Python 3.13 added uuid.uuid7() to the standard library:
import uuid
id_v7 = uuid.uuid7()
print(id_v7) # 018f3c4e-7a21-7b3c-9d4e-5f6a7b8c9d0e
The first 48 bits are a Unix millisecond timestamp, so IDs generated later sort after earlier ones — both as strings and as bytes.
Python 3.9–3.12: the uuid6 package
For older Python versions, install the uuid6 backport:
pip install uuid6
import uuid6
id_v7 = uuid6.uuid7()
print(id_v7) # 018f3c4e-7a21-7b3c-9d4e-5f6a7b8c9d0e
print(type(id_v7)) # <class 'uuid.UUID'> — same UUID type
uuid6.uuid7() returns a standard uuid.UUID object, so it is a drop-in complement to the standard library.
Parsing and validating UUIDs
import uuid
raw = "550e8400-e29b-41d4-a716-446655440000"
# Parse — raises ValueError if invalid
parsed = uuid.UUID(raw)
print(parsed.version) # 4
# Safe parse
def parse_uuid(s: str) -> uuid.UUID | None:
try:
return uuid.UUID(s)
except ValueError:
return None
# Check version
def is_v7(s: str) -> bool:
try:
return uuid.UUID(s).version == 7
except ValueError:
return False
uuid.UUID(string) validates the format and raises ValueError on invalid input — use it at system boundaries (API input, database reads from untrusted sources).
Name-based UUIDs: v5
UUID v5 produces a deterministic identifier from a namespace and a name. Same inputs always give the same UUID — useful for deriving stable IDs from existing data:
import uuid
# RFC 9562 standard namespaces
ns_url = uuid.NAMESPACE_URL
ns_dns = uuid.NAMESPACE_DNS
# Stable ID for a URL — reproducible anywhere
page_id = uuid.uuid5(ns_url, "https://example.com/products/123")
print(page_id) # always the same value
# Stable ID for a domain name
domain_id = uuid.uuid5(ns_dns, "example.com")
Use v5 when you need a UUID that can be recalculated from its inputs without a lookup table — content IDs, product identifiers derived from SKUs, stable keys for imported data.
Django
Django’s UUIDField maps to a native UUID column in PostgreSQL and a CHAR(32) in other databases:
import uuid
from django.db import models
class Event(models.Model):
id = models.UUIDField(
primary_key=True,
default=uuid.uuid4, # called without () — Django calls it per row
editable=False,
)
name = models.CharField(max_length=255)
created_at = models.DateTimeField(auto_now_add=True)
For UUID v7 as the primary key (Django + PostgreSQL 18):
import uuid6
from django.db import models
class Order(models.Model):
id = models.UUIDField(
primary_key=True,
default=uuid6.uuid7, # time-sortable, index-friendly
editable=False,
)
total = models.DecimalField(max_digits=10, decimal_places=2)
Using v7 as a Django primary key on PostgreSQL gives the same B-tree performance benefits as with any other ORM — consecutive inserts cluster on the same index leaf page.
SQLAlchemy
SQLAlchemy has a native Uuid type (SQLAlchemy 2.0+) that maps to the appropriate database-level UUID type:
import uuid
from sqlalchemy import String
from sqlalchemy.orm import DeclarativeBase, mapped_column, Mapped
from sqlalchemy.dialects.postgresql import UUID as PG_UUID
class Base(DeclarativeBase):
pass
class User(Base):
__tablename__ = "users"
# SQLAlchemy 2.0+ — database-native UUID type
id: Mapped[uuid.UUID] = mapped_column(
PG_UUID(as_uuid=True),
primary_key=True,
default=uuid.uuid4,
)
name: Mapped[str] = mapped_column(String(255))
For UUID v7 with SQLAlchemy on PostgreSQL 18, use a server-side default:
from sqlalchemy import text
class Event(Base):
__tablename__ = "events"
id: Mapped[uuid.UUID] = mapped_column(
PG_UUID(as_uuid=True),
primary_key=True,
server_default=text("uuidv7()"), # PostgreSQL 18+
)
name: Mapped[str] = mapped_column(String(255))
For older PostgreSQL, generate v7 in Python and pass it in:
import uuid6
from sqlalchemy.orm import Session
with Session(engine) as session:
event = Event(id=uuid6.uuid7(), name="page_view")
session.add(event)
session.commit()
Pydantic
Pydantic v2 validates UUID fields automatically:
import uuid
from pydantic import BaseModel
class UserCreate(BaseModel):
id: uuid.UUID
name: str
# Pydantic accepts both UUID objects and UUID strings
user = UserCreate(id="550e8400-e29b-41d4-a716-446655440000", name="Alice")
print(user.id) # UUID('550e8400-e29b-41d4-a716-446655440000')
print(type(user.id)) # <class 'uuid.UUID'>
For response serialisation, configure Pydantic to output UUIDs as strings:
from pydantic import BaseModel, ConfigDict
class UserResponse(BaseModel):
model_config = ConfigDict(json_encoders={uuid.UUID: str})
id: uuid.UUID
name: str
FastAPI
FastAPI uses Pydantic for request/response validation, so UUID handling is automatic:
import uuid
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
id: uuid.UUID
name: str
@app.post("/items", response_model=Item)
async def create_item(name: str):
return Item(id=uuid.uuid4(), name=name)
@app.get("/items/{item_id}", response_model=Item)
async def get_item(item_id: uuid.UUID):
# FastAPI validates and parses item_id automatically
# Returns 422 if the path parameter is not a valid UUID
...
Path parameters typed as uuid.UUID are validated automatically — FastAPI returns a 422 Unprocessable Entity if the client sends a malformed value, with no manual validation code needed.
Common mistakes
- Calling
uuid.uuid4with parentheses in a default argument.default=uuid.uuid4()generates one UUID at class definition time and reuses it for every row. Usedefault=uuid.uuid4(no parentheses) so Django and SQLAlchemy call it once per instance. - Storing as a string instead of bytes.
VARCHAR(36)costs 2.25× more space than a nativeuuidorBINARY(16)column. Use the native type. - Using
uuid.uuid1()for new work. v1 embeds the MAC address of the machine. Use v4 or v7 instead. - Comparing UUID objects with strings.
uuid.UUID("...") == "..."is alwaysFalse. Convert both sides to the same type before comparing.
For how UUIDs are stored in databases, see UUID in databases. To compare v4 and v7 in depth, see UUID v4 vs UUID v7.
Frequently asked questions
How do I generate a UUID in Python?
Import the built-in uuid module and call uuid.uuid4() for a random v4, or uuid.uuid7() for a time-ordered v7 (Python 3.13+). For Python 3.9–3.12 use the uuid6 package: pip install uuid6, then uuid6.uuid7().
Does Python support UUID v7?
Yes, from Python 3.13 via uuid.uuid7() in the standard library. For Python 3.9–3.12, install the uuid6 package which provides the same interface.
What column type should I use to store a UUID in a database?
Use the native type where one exists — uuid in PostgreSQL, uniqueidentifier in SQL Server. In MySQL use BINARY(16) and store raw bytes. Avoid VARCHAR(36): it costs 36 bytes per row versus 16 for binary and makes every comparison a string operation.
Does Python have a built-in UUID generator?
Yes. The uuid module is part of Python's standard library and requires no installation. uuid.uuid4() generates a random UUID v4, and uuid.uuid7() is available from Python 3.13. For earlier versions, use the uuid6 package for v7 support.
How do I generate a UUID in Python?
Import the uuid module and call uuid.uuid4() for a random UUID v4, or uuid.uuid7() for a time-ordered UUID v7 (Python 3.13+). Both return a UUID object; call str() on it or use the .hex attribute for different string formats.
Does Python support UUID v7?
Yes, from Python 3.13 via uuid.uuid7(). For Python 3.9–3.12, install the uuid6 package (pip install uuid6) which provides uuid6.uuid7() with the same interface.
How do I use UUIDs with Django?
Django has a built-in UUIDField that stores UUIDs as 16-byte binary values in the database and presents them as Python UUID objects. Set default=uuid.uuid4 (without parentheses) for auto-generation on new rows.