Cookbook¶
Practical recipes for common TypedUUID use cases.
Basic Recipes¶
Creating a Set of Related ID Types¶
from typed_uuid import create_typed_uuid_class
# E-commerce domain
UserUUID = create_typed_uuid_class('User', 'user')
OrderUUID = create_typed_uuid_class('Order', 'order')
ProductUUID = create_typed_uuid_class('Product', 'product')
CartUUID = create_typed_uuid_class('Cart', 'cart')
PaymentUUID = create_typed_uuid_class('Payment', 'payment')
# Content management domain
ArticleUUID = create_typed_uuid_class('Article', 'article')
AuthorUUID = create_typed_uuid_class('Author', 'author')
CommentUUID = create_typed_uuid_class('Comment', 'comment')
TagUUID = create_typed_uuid_class('Tag', 'tag')
ID Factory Function¶
from typed_uuid import create_typed_uuid_class
def create_id_types(*names):
"""Create multiple ID types at once."""
types = {}
for name in names:
type_id = name.lower()
types[f'{name}UUID'] = create_typed_uuid_class(name, type_id)
return types
# Usage
ids = create_id_types('User', 'Order', 'Product')
UserUUID = ids['UserUUID']
OrderUUID = ids['OrderUUID']
Safe ID Parsing¶
from typed_uuid import TypedUUID, InvalidUUIDError, InvalidTypeIDError
from typing import Optional
def safe_parse(value: str, expected_type=None) -> Optional[TypedUUID]:
"""Safely parse a UUID string, returning None on failure."""
try:
parsed = TypedUUID.parse(value)
if expected_type and not isinstance(parsed, expected_type):
return None
return parsed
except (InvalidUUIDError, InvalidTypeIDError):
return None
# Usage
user_id = safe_parse('user-550e8400-e29b-41d4-a716-446655440000', UserUUID)
if user_id:
print(f"Valid user ID: {user_id}")
else:
print("Invalid or wrong type")
Web Application Recipes¶
Flask Route with TypedUUID¶
from flask import Flask, jsonify, abort
from typed_uuid import create_typed_uuid_class, InvalidUUIDError
app = Flask(__name__)
UserUUID = create_typed_uuid_class('User', 'user')
@app.route('/users/<user_id>')
def get_user(user_id: str):
try:
parsed_id = UserUUID.from_string(user_id)
except InvalidUUIDError:
abort(400, description="Invalid user ID")
# Fetch user from database
user = db.get_user(parsed_id)
if not user:
abort(404, description="User not found")
return jsonify({
'id': str(user.id),
'name': user.name
})
Django Model with TypedUUID¶
from django.db import models
from typed_uuid import create_typed_uuid_class
UserUUID = create_typed_uuid_class('User', 'user')
class User(models.Model):
id = models.CharField(max_length=50, primary_key=True)
name = models.CharField(max_length=100)
def save(self, *args, **kwargs):
if not self.id:
self.id = str(UserUUID())
super().save(*args, **kwargs)
@property
def typed_id(self):
return UserUUID.from_string(self.id)
GraphQL Resolver¶
import strawberry
from typed_uuid import create_typed_uuid_class
UserUUID = create_typed_uuid_class('User', 'user')
@strawberry.type
class User:
id: str
name: str
@strawberry.type
class Query:
@strawberry.field
def user(self, id: str) -> User:
user_id = UserUUID.from_string(id)
# Fetch and return user
return User(id=str(user_id), name="Alice")
Database Recipes¶
Bulk Insert with TypedUUIDs¶
from typed_uuid import create_typed_uuid_class
UserUUID = create_typed_uuid_class('User', 'user')
def bulk_create_users(names: list[str]):
"""Create multiple users with generated IDs."""
users = [
{'id': str(UserUUID()), 'name': name}
for name in names
]
cursor.executemany(
"INSERT INTO users (id, name) VALUES (%(id)s, %(name)s)",
users
)
return users
Migration from Plain UUIDs¶
from uuid import UUID
from typed_uuid import create_typed_uuid_class
UserUUID = create_typed_uuid_class('User', 'user')
def migrate_user_ids():
"""Migrate existing plain UUIDs to TypedUUIDs."""
cursor.execute("SELECT id FROM users_old")
for row in cursor.fetchall():
old_id = row[0] # Plain UUID string
new_id = UserUUID(old_id) # Wrap in TypedUUID
cursor.execute(
"UPDATE users SET id = %s WHERE id = %s",
(str(new_id), old_id)
)
Serialization Recipes¶
Custom JSON Encoder¶
import json
from typing import Any
from typed_uuid import TypedUUID
class AppJSONEncoder(json.JSONEncoder):
"""JSON encoder that handles TypedUUIDs and other custom types."""
def default(self, obj: Any) -> Any:
if isinstance(obj, TypedUUID):
return obj.short # Use short format for APIs
if hasattr(obj, '__json__'):
return obj.__json__()
return super().default(obj)
# Usage
data = {'user_id': UserUUID(), 'timestamp': '2024-01-15'}
json.dumps(data, cls=AppJSONEncoder)
Dataclass with TypedUUID¶
from dataclasses import dataclass, field
from typed_uuid import create_typed_uuid_class
UserUUID = create_typed_uuid_class('User', 'user')
@dataclass
class User:
name: str
email: str
id: UserUUID = field(default_factory=UserUUID)
def to_dict(self):
return {
'id': str(self.id),
'name': self.name,
'email': self.email
}
user = User(name='Alice', email='alice@example.com')
print(user.id) # user-550e8400-...
Testing Recipes¶
Fixture for TypedUUIDs¶
import pytest
from typed_uuid import create_typed_uuid_class
@pytest.fixture
def user_uuid_class():
return create_typed_uuid_class('User', 'testuser')
@pytest.fixture
def sample_user_id(user_uuid_class):
return user_uuid_class('550e8400-e29b-41d4-a716-446655440000')
def test_user_creation(sample_user_id):
assert str(sample_user_id) == 'testuser-550e8400-e29b-41d4-a716-446655440000'
Mocking TypedUUIDs¶
from unittest.mock import patch
from uuid import UUID
def test_with_predictable_uuid():
fixed_uuid = UUID('550e8400-e29b-41d4-a716-446655440000')
with patch('uuid.uuid4', return_value=fixed_uuid):
user_id = UserUUID()
assert str(user_id) == 'user-550e8400-e29b-41d4-a716-446655440000'
Parameterized Tests¶
import pytest
from typed_uuid import create_typed_uuid_class, InvalidUUIDError
UserUUID = create_typed_uuid_class('User', 'user')
@pytest.mark.parametrize("input_str,should_succeed", [
('user-550e8400-e29b-41d4-a716-446655440000', True),
('550e8400-e29b-41d4-a716-446655440000', True),
('USER-550e8400-e29b-41d4-a716-446655440000', False), # Case sensitive
('invalid', False),
('', False),
])
def test_parsing(input_str, should_succeed):
if should_succeed:
result = UserUUID.from_string(input_str)
assert isinstance(result, UserUUID)
else:
with pytest.raises((InvalidUUIDError, Exception)):
UserUUID.from_string(input_str)
Logging Recipes¶
Structured Logging¶
import logging
import json
from typed_uuid import TypedUUID
class TypedUUIDLogFormatter(logging.Formatter):
def format(self, record):
# Convert any TypedUUIDs in the message to strings
if hasattr(record, 'user_id') and isinstance(record.user_id, TypedUUID):
record.user_id = str(record.user_id)
return super().format(record)
# Usage
logger = logging.getLogger(__name__)
logger.info("User logged in", extra={'user_id': user_id})
Request Context¶
from contextvars import ContextVar
from typed_uuid import TypedUUID
request_id_var: ContextVar[TypedUUID] = ContextVar('request_id')
def set_request_id():
RequestUUID = create_typed_uuid_class('Request', 'req')
request_id_var.set(RequestUUID())
def get_request_id() -> str:
return str(request_id_var.get())