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

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.