Framework Integration Guide
This guide covers integration with popular Python frameworks.
SQLAlchemy Integration
Converting to SQLAlchemy
from sqlalchemy import MetaData, create_engine
from sqlmeta import Table, SqlColumn
from sqlmeta.adapters.sqlalchemy import to_sqlalchemy
# Define sqlmeta table
table = Table("users", columns=[
SqlColumn("id", "INTEGER", is_primary_key=True),
SqlColumn("email", "VARCHAR(255)", is_nullable=False),
SqlColumn("name", "VARCHAR(100)"),
])
# Convert to SQLAlchemy
metadata = MetaData()
sa_table = to_sqlalchemy(table, metadata)
# Use with SQLAlchemy
engine = create_engine("postgresql://localhost/mydb")
metadata.create_all(engine)
Converting from SQLAlchemy
from sqlalchemy import Table as SATable, Column, Integer, String, MetaData
from sqlmeta.adapters.sqlalchemy import from_sqlalchemy
# Define SQLAlchemy table
metadata = MetaData()
sa_table = SATable(
'users', metadata,
Column('id', Integer, primary_key=True),
Column('email', String(255), nullable=False),
Column('name', String(100)),
)
# Convert to sqlmeta
sqlmeta_table = from_sqlalchemy(sa_table)
# Export to JSON
import json
with open('schema.json', 'w') as f:
json.dump(sqlmeta_table.to_dict(), f, indent=2)
Pydantic Integration
Generating Models
from sqlmeta import Table, SqlColumn
from sqlmeta.adapters.pydantic import to_pydantic
# Define table
users_table = Table("users", columns=[
SqlColumn("id", "INTEGER", is_primary_key=True),
SqlColumn("email", "VARCHAR(255)", is_nullable=False),
SqlColumn("name", "VARCHAR(100)"),
SqlColumn("age", "INTEGER"),
])
# Generate Pydantic model
UserModel = to_pydantic(users_table)
# Use the model
user = UserModel(
id=1,
email="user@example.com",
name="John Doe",
age=30
)
# Validation
print(user.model_dump_json())
Custom Model Names
# Custom model name
CustomUser = to_pydantic(users_table, model_name="CustomUser")
# Disable PascalCase conversion
user_model = to_pydantic(users_table, use_title_case=False)
Schema Generation
from sqlmeta.adapters.pydantic import to_pydantic_schema
# Generate JSON schema
schema = to_pydantic_schema(users_table)
# Use for API documentation
print(json.dumps(schema, indent=2))
Alembic Integration
Generating Operations
from sqlmeta import Table, SqlColumn
from sqlmeta.adapters.alembic import generate_operations
# Define old and new schemas
old_table = Table("users", columns=[
SqlColumn("id", "INTEGER", is_primary_key=True),
SqlColumn("name", "VARCHAR(100)"),
])
new_table = Table("users", columns=[
SqlColumn("id", "INTEGER", is_primary_key=True),
SqlColumn("name", "VARCHAR(100)"),
SqlColumn("email", "VARCHAR(255)", is_nullable=False),
])
# Generate operations
operations = generate_operations(
source_table=old_table,
target_table=new_table,
dialect="postgresql"
)
# Use in Alembic migration
def upgrade():
for op in operations:
op.execute()
Complete Migration Script
from sqlmeta.adapters.alembic import generate_migration_script
source_schema = [old_users_table, old_posts_table]
target_schema = [new_users_table, new_posts_table, comments_table]
script = generate_migration_script(
source_tables=source_schema,
target_tables=target_schema,
dialect="postgresql",
message="Add comments table and update users"
)
# Save to Alembic versions directory
with open("alembic/versions/001_migration.py", "w") as f:
f.write(script)
Using with Alembic Environment
# In alembic/env.py
from sqlmeta import Table
from sqlmeta.adapters.sqlalchemy import to_sqlalchemy
# Load your sqlmeta schema
schema = load_schema() # Your function to load schema
# Convert to SQLAlchemy metadata
target_metadata = MetaData()
for table in schema:
to_sqlalchemy(table, target_metadata)
# Use with Alembic autogenerate
context.configure(
connection=connection,
target_metadata=target_metadata,
# ...
)
Combined Example: Full Stack
from sqlmeta import Table, SqlColumn, SqlConstraint, ConstraintType
from sqlmeta.adapters.sqlalchemy import to_sqlalchemy
from sqlmeta.adapters.pydantic import to_pydantic
from sqlmeta.adapters.alembic import generate_operations
from sqlalchemy import MetaData, create_engine
from sqlalchemy.orm import sessionmaker
# 1. Define schema in sqlmeta
users_table = Table(
"users",
dialect="postgresql",
columns=[
SqlColumn("id", "SERIAL", is_primary_key=True),
SqlColumn("email", "VARCHAR(255)", is_nullable=False),
SqlColumn("name", "VARCHAR(100)", is_nullable=False),
SqlColumn("created_at", "TIMESTAMP", default_value="CURRENT_TIMESTAMP"),
],
constraints=[
SqlConstraint(
constraint_type=ConstraintType.UNIQUE,
name="uq_users_email",
column_names=["email"]
)
]
)
# 2. Create database schema with SQLAlchemy
metadata = MetaData()
sa_table = to_sqlalchemy(users_table, metadata)
engine = create_engine("postgresql://localhost/mydb")
metadata.create_all(engine)
# 3. Generate Pydantic model for API
UserModel = to_pydantic(users_table)
# 4. Use in FastAPI
from fastapi import FastAPI
app = FastAPI()
@app.post("/users/", response_model=UserModel)
async def create_user(user: UserModel):
# Save to database
return user
# 5. Later: Update schema and generate migration
users_table_v2 = Table(
"users",
dialect="postgresql",
columns=[
SqlColumn("id", "SERIAL", is_primary_key=True),
SqlColumn("email", "VARCHAR(255)", is_nullable=False),
SqlColumn("name", "VARCHAR(100)", is_nullable=False),
SqlColumn("created_at", "TIMESTAMP", default_value="CURRENT_TIMESTAMP"),
SqlColumn("last_login", "TIMESTAMP"), # New column
],
constraints=[
SqlConstraint(
constraint_type=ConstraintType.UNIQUE,
name="uq_users_email",
column_names=["email"]
)
]
)
# Generate Alembic migration
operations = generate_operations(
source_table=users_table,
target_table=users_table_v2,
dialect="postgresql"
)
Django Integration (Custom)
While sqlmeta doesn’t have a built-in Django adapter, you can integrate it:
from sqlmeta import Table, SqlColumn
from django.db import models
def to_django_model(table: Table, model_name: str = None):
"""Convert sqlmeta Table to Django model."""
if model_name is None:
model_name = table.name.title()
fields = {}
for col in table.columns:
django_field = _map_to_django_field(col)
fields[col.name] = django_field
# Create model class dynamically
model_class = type(
model_name,
(models.Model,),
{
**fields,
'__module__': '__main__',
'Meta': type('Meta', (), {
'db_table': table.name,
'app_label': 'myapp',
})
}
)
return model_class
def _map_to_django_field(col):
"""Map sqlmeta column to Django field."""
type_map = {
'INTEGER': models.IntegerField,
'VARCHAR': models.CharField,
'TEXT': models.TextField,
'TIMESTAMP': models.DateTimeField,
'BOOLEAN': models.BooleanField,
}
base_type = col.data_type.split('(')[0].upper()
field_class = type_map.get(base_type, models.CharField)
kwargs = {}
if col.is_primary_key:
kwargs['primary_key'] = True
if col.nullable:
kwargs['null'] = True
if col.default_value:
kwargs['default'] = col.default_value
# Handle CharField max_length
if field_class == models.CharField:
if '(' in col.data_type:
length = col.data_type.split('(')[1].split(')')[0]
kwargs['max_length'] = int(length)
else:
kwargs['max_length'] = 255
return field_class(**kwargs)
Best Practices
Single Source of Truth
Define your schema once in sqlmeta and generate everything else:
# schema.py schema = [users_table, posts_table, comments_table] # Export to SQLAlchemy def get_sqlalchemy_metadata(): metadata = MetaData() for table in schema: to_sqlalchemy(table, metadata) return metadata # Export to Pydantic def get_pydantic_models(): return { 'User': to_pydantic(users_table), 'Post': to_pydantic(posts_table), 'Comment': to_pydantic(comments_table), }
Version Control Your Schema
Store schema definitions in version control:
# schemas/v1.py users_v1 = Table(...) # schemas/v2.py users_v2 = Table(...) # Then generate migrations from schemas.v1 import users_v1 from schemas.v2 import users_v2 operations = generate_operations(users_v1, users_v2)
Automate Schema Updates
Create scripts to regenerate models when schema changes:
#!/bin/bash # update_models.sh python generate_sqlalchemy.py python generate_pydantic.py python generate_migration.py
Test Integrations
Test that conversions preserve schema semantics:
def test_roundtrip(): # sqlmeta -> SQLAlchemy -> sqlmeta sa_table = to_sqlalchemy(original_table, MetaData()) roundtrip_table = from_sqlalchemy(sa_table) # Compare comparator = ObjectComparator() diff = comparator.compare_tables(original_table, roundtrip_table) assert not diff.has_diffs