Как получить общий тип данных из конкретного типа диалекта в sqlalchemy?

Я хочу создать функцию, которая сопоставляет специфичные для диалекта типы БД с общими типами БД sqlalchemy.

Например: я пытаюсь получить типы данных из таблицы OracleDB, а затем создать таблицу в MySql DB (или другой), используя полученные метаданные. Я получаю типы столбцов, зависящие от диалекта.

Вот минимальный код для создания таблицы OracleDB:

create table test_test(id int, num_int number(10), num_float number(10,4), dt date, ts timestamp, str varchar2(100), fl float);

insert into test_test(id) values(1);

Чтобы получить типы столбцов таблицы БД

from pprint import pprint
from sqlalchemy import sessionmaker, create_engine

engine = create_engine(...)
session = sessionmaker()
session.configure(bind=engine)
session = session()
res = session.execute('select * from test_test where 1 = 2')
columns = res.cursor.description
pprint(columns)

Выход

[('ID', <cx_Oracle.DbType DB_TYPE_NUMBER>, 39, None, 38, 0, 1),
 ('NUM_INT', <cx_Oracle.DbType DB_TYPE_NUMBER>, 11, None, 10, 0, 1),
 ('NUM_FLOAT', <cx_Oracle.DbType DB_TYPE_NUMBER>, 16, None, 10, 4, 1),
 ('DT', <cx_Oracle.DbType DB_TYPE_DATE>, 23, None, None, None, 1),
 ('TS', <cx_Oracle.DbType DB_TYPE_TIMESTAMP>, 23, None, 0, 6, 1),
 ('STR', <cx_Oracle.DbType DB_TYPE_VARCHAR>, 100, 100, None, None, 1),
 ('FL', <cx_Oracle.DbType DB_TYPE_NUMBER>, 127, None, 126, -127, 1)]

Как я могу получить общие типы столбцов sqlalchemy (String, Integer и т. д.) вместо специфичных для диалекта?


person Yuriy Stakhniak    schedule 23.12.2020    source источник
comment
Дубликат stackoverflow.com/q/64214225/2144390   -  person Gord Thompson    schedule 24.12.2020
comment
@GordThompson, к сожалению, решение, предлагаемое здесь, не будет работать для типов данных, специфичных для драйвера, таких как cx_Oracle.DbType.DB_TYPE_VARCHAR   -  person MaxU    schedule 01.01.2021
comment
@MaxU re: не будет работать для типов данных, специфичных для драйвера. Если вы считаете, что это недостаток в реализации SQLAlchemy .as_generic(), откройте выпуск на GitHub.   -  person Gord Thompson    schedule 01.01.2021
comment
@GordThompson, хорошо, мы попробуем открыть новую проблему GitHib после истечения срока действия награды ...   -  person MaxU    schedule 04.01.2021


Ответы (1)


Метод .as_generic(), добавленный в SQLAlchemy 1.4 через этот запрос на включение, может обрабатывать типы Oracle, определенные SQLAlchemy. себя на уровне диалекта…

from sqlalchemy.dialects.oracle import VARCHAR
oracle_type = VARCHAR()
print(type(oracle_type))  # <class 'sqlalchemy.sql.sqltypes.VARCHAR'>
generic_type = oracle_type.as_generic()
print(type(generic_type))  # <class 'sqlalchemy.sql.sqltypes.String'>

… но он недоступен для типов уровня драйвера, если только сам драйвер не реализует его:

from cx_Oracle import  DB_TYPE_VARCHAR
cx_oracle_type = DB_TYPE_VARCHAR
print(type(cx_oracle_type))  # <class 'cx_Oracle.DbType'>
generic_type = cx_oracle_type.as_generic()
# AttributeError: 'cx_Oracle.DbType' object has no attribute 'as_generic'

Итак, если cx-Oracle не реализует такой метод (сомнительно), вам, вероятно, придется свернуть свою собственную функцию, чтобы сделать это:

def cx_oracle_type_as_generic(type_):
    import sqlalchemy as sa
    if type_.name == "DB_TYPE_VARCHAR":
        return sa.sql.sqltypes.String()
    elif type_.name == "DB_TYPE_NVARCHAR":
        return sa.sql.sqltypes.Unicode()
    # … and so on …
    else:
        raise NotImplementedError


from cx_Oracle import  DB_TYPE_VARCHAR
cx_oracle_type = DB_TYPE_VARCHAR
print(type(cx_oracle_type))  # <class 'cx_Oracle.DbType'>
generic_type = cx_oracle_type_as_generic(cx_oracle_type)
print(type(generic_type))  # <class 'sqlalchemy.sql.sqltypes.String'>
person Gord Thompson    schedule 01.01.2021