SQLAlchemy

SQLAlchemy is a popular Python library for working with databases, providing both ORM (Object-Relational Mapping) and Core API for executing SQL queries.

YDB supports integration with SQLAlchemy through a special ydb-sqlalchemy dialect, which provides full compatibility with SQLAlchemy 2.0 and partial support for SQLAlchemy 1.4.

Installation

Install the ydb-sqlalchemy package using pip:

pip install ydb-sqlalchemy

Quick Start

Synchronous Connection

import sqlalchemy as sa

# Create engine
engine = sa.create_engine("yql+ydb://localhost:2136/local")

# Execute query
with engine.connect() as conn:
    result = conn.execute(sa.text("SELECT 1 AS value"))
    print(result.fetchone())

Asynchronous Connection

import asyncio
import sqlalchemy as sa
from sqlalchemy.ext.asyncio import create_async_engine

async def main():
    # Create async engine
    engine = create_async_engine("yql+ydb_async://localhost:2136/local")

    # Execute query
    async with engine.connect() as conn:
        result = await conn.execute(sa.text("SELECT 1 AS value"))
        print(await result.fetchone())

asyncio.run(main())

Connection Configuration

Authentication Methods

Anonymous Access

For local development or testing:

import sqlalchemy as sa

engine = sa.create_engine("yql+ydb://localhost:2136/local")

Static Credentials

Use username and password authentication:

engine = sa.create_engine(
    "yql+ydb://localhost:2136/local",
    connect_args={
        "credentials": {
            "username": "your_username",
            "password": "your_password"
        }
    }
)

Token Authentication

Use access token for authentication:

engine = sa.create_engine(
    "yql+ydb://localhost:2136/local",
    connect_args={
        "credentials": {
            "token": "your_access_token"
        }
    }
)

Service Account Authentication

Use service account JSON key:

import json

# Load from file
with open('service_account_key.json', 'r') as f:
    service_account_json = json.load(f)

engine = sa.create_engine(
    "yql+ydb://localhost:2136/local",
    connect_args={
        "credentials": {
            "service_account_json": service_account_json
        }
    }
)

# Or pass JSON directly
engine = sa.create_engine(
    "yql+ydb://localhost:2136/local",
    connect_args={
        "credentials": {
            "service_account_json": {
                "id": "your_key_id",
                "service_account_id": "your_service_account_id",
                "created_at": "2023-01-01T00:00:00Z",
                "key_algorithm": "RSA_2048",
                "public_key": "-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----",
                "private_key": "-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----"
            }
        }
    }
)

Using YDB SDK Credentials

You can use any available authentication methods from YDB Python SDK:

import ydb.iam

# Metadata service
engine = sa.create_engine(
    "yql+ydb://localhost:2136/local",
    connect_args={
        "credentials": ydb.iam.MetadataUrlCredentials()
    }
)

# OAuth token
engine = sa.create_engine(
    "yql+ydb://localhost:2136/local",
    connect_args={
        "credentials": ydb.iam.OAuthCredentials("your_oauth_token")
    }
)

# Static credentials
engine = sa.create_engine(
    "yql+ydb://localhost:2136/local",
    connect_args={
        "credentials": ydb.iam.StaticCredentials("username", "password")
    }
)

TLS Configuration

For secure connections to YDB:

engine = sa.create_engine(
    "yql+ydb://ydb.example.com:2135/prod",
    connect_args={
        "credentials": {"token": "your_token"},
        "protocol": "grpc",
        "root_certificates_path": "/path/to/ca-certificates.crt",
        # "root_certificates": crt_string,  # Alternative - certificate string
    }
)

Supported Data Types

YDB SQLAlchemy provides comprehensive support for YDB data types through custom SQLAlchemy types. For detailed information about YDB data types, see the YDB Data Types Documentation.

Type Mapping Summary

YDB Type YDB SQLAlchemy Type Standard SQLAlchemy Type Python Type Notes
Bool Boolean Boolean bool
Int8 Int8 int -2^7 to 2^7-1
Int16 Int16 int -2^15 to 2^15-1
Int32 Int32 int -2^31 to 2^31-1
Int64 Int64 Integer int -2^63 to 2^63-1
Uint8 UInt8 int 0 to 2^8-1
Uint16 UInt16 int 0 to 2^16-1
Uint32 UInt32 int 0 to 2^32-1
Uint64 UInt64 int 0 to 2^64-1
Float Float Float float
Double Double float Available in SQLAlchemy 2.0+
Decimal(p,s) Decimal DECIMAL decimal.Decimal
String BINARY bytes
Utf8 String/Text str
Date YqlDate Date datetime.date
Date32 YqlDate32 datetime.date Extended date range support
Datetime YqlDateTime DATETIME datetime.datetime
Datetime64 YqlDateTime64 datetime.datetime Extended range
Timestamp YqlTimestamp TIMESTAMP datetime.datetime
Timestamp64 YqlTimestamp64 datetime.datetime Extended range
Json YqlJSON JSON dict/list
List<T> ListType ARRAY list
Struct<...> StructType dict
Optional<T> nullable=True None + base type

Migrations with Alembic

YDB-Specific Configuration

YDB requires special configuration in env.py due to its unique characteristics:

# migrations/env.py
from logging.config import fileConfig
import sqlalchemy as sa
from sqlalchemy import engine_from_config, pool
from alembic import context
from alembic.ddl.impl import DefaultImpl

# Import your models
from myapp.models import Base

config = context.config

if config.config_file_name is not None:
    fileConfig(config.config_file_name)

target_metadata = Base.metadata

# YDB-specific implementation
class YDBImpl(DefaultImpl):
    __dialect__ = "yql"

def run_migrations_offline() -> None:
    """Run migrations in 'offline' mode."""
    url = config.get_main_option("sqlalchemy.url")
    context.configure(
        url=url,
        target_metadata=target_metadata,
        literal_binds=True,
        dialect_opts={"paramstyle": "named"},
    )

    with context.begin_transaction():
        context.run_migrations()

def run_migrations_online() -> None:
    """Run migrations in 'online' mode."""
    connectable = engine_from_config(
        config.get_section(config.config_ini_section, {}),
        prefix="sqlalchemy.",
        poolclass=pool.NullPool,
    )

    with connectable.connect() as connection:
        context.configure(
            connection=connection,
            target_metadata=target_metadata
        )

        with context.begin_transaction():
            context.run_migrations()

if context.is_offline_mode():
    run_migrations_offline()
else:
    run_migrations_online()
Previous
Next