Source code for app.models.custom_types
"""
Cross-dialect custom SQLAlchemy types for PostgreSQL and SQLite compatibility.
"""
import json
from sqlalchemy.types import TypeDecorator, TEXT
[docs]
class TextArray(TypeDecorator):
"""
SQLAlchemy type decorator that uses native PostgreSQL ARRAY(TEXT)
and serializes to JSON string in SQLite.
"""
impl = TEXT
cache_ok = True
[docs]
def load_dialect_impl(self, dialect):
if dialect.name == "postgresql":
from sqlalchemy.dialects.postgresql import ARRAY, TEXT as pgTEXT
return dialect.type_descriptor(ARRAY(pgTEXT))
return dialect.type_descriptor(TEXT)
[docs]
def process_bind_param(self, value, dialect):
if dialect.name == "postgresql":
return value
if value is None:
return None
return json.dumps(value)
[docs]
def process_result_value(self, value, dialect):
if dialect.name == "postgresql":
return value
if value is None:
return None
try:
return json.loads(value)
except Exception:
return []
[docs]
class IntegerArray(TypeDecorator):
"""
SQLAlchemy type decorator that uses native PostgreSQL ARRAY(INTEGER)
and serializes to JSON string in SQLite.
"""
impl = TEXT
cache_ok = True
[docs]
def load_dialect_impl(self, dialect):
if dialect.name == "postgresql":
from sqlalchemy.dialects.postgresql import ARRAY, INTEGER as pgINT
return dialect.type_descriptor(ARRAY(pgINT))
return dialect.type_descriptor(TEXT)
[docs]
def process_bind_param(self, value, dialect):
if dialect.name == "postgresql":
return value
if value is None:
return None
return json.dumps(value)
[docs]
def process_result_value(self, value, dialect):
if dialect.name == "postgresql":
return value
if value is None:
return None
try:
return json.loads(value)
except Exception:
return []