Как да получите общ тип данни от специфичен за диалект тип в sqlalchemy?

Искам да създам функция, която преобразува специфични за диалекти DB типове към общи sqlalchemy DB типове.

Например: Опитвам се да получа типове данни от таблицата на 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);

За получаване на типове колони на таблица в DB

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 (низ, цяло число и т.н.) вместо специфични за диалекта?


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: няма да работи за типове данни, специфични за драйвера - Ако смятате, че това е недостатък в внедряването на .as_generic() от SQLAlchemy, моля, отворете проблем в 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