Есть ли способ создать динамическую таблицу из словаря, указанного в файле yaml? Я определяю много конфигурации ETL в файле yaml, поэтому мне было любопытно, могу ли я также добавить к нему аспект создания таблицы, поэтому мне не нужно модифицировать отдельный файл.sql в отдельном каталоге.
database:
table: 'schema.fact_stuff'
create_columns: [
{}
] #not sure how this section should be
Я нашел решение в stackoverflow, которое сместило некоторые списки вместе, что-то похожее, но я бы предпочел явно определить каждый столбец.
{'column_name': 'id', 'column_type': Integer, 'primary_key': False, 'nullable': True}
Я закончил работу с этим:
from sqlalchemy.types import (Integer, NUMERIC, TEXT, BOOLEAN, TIMESTAMP, DATE)
sql_types = {'integer': Integer,
'numeric': NUMERIC,
'text': TEXT,
'date': DATE,
'timestamp': TIMESTAMP(timezone=False),
'timestamptz': TIMESTAMP(timezone=True)}
exclude_list = ['original_name']
table_dict = [{k: v for k, v in d.items() if k not in exclude_list} for d in c[variable]['load']['columns']]
for column in table_dict:
for key, val in column.copy().items():
if key == 'type_':
column[key] = sql_types[val]
elif key == 'default':
column[key] = dt.datetime.utcnow
metadata = sa.MetaData(schema=c[variable]['load']['schema'])
metadata.reflect(bind=engine, autoload=True)
fact = sa.Table(c[variable]['load']['table'], metadata, extend_existing=True,
*(sa.Column(**kwargs) for kwargs in table_dict))
fact.create_all(engine, checkfirst=True)
Но затем я перешел к тому, чтобы pandas определял dtypes вместо того, чтобы определять их в файле yaml. Это создает sql с шаблоном jinja2, и я запускаю все мои источники данных для создания DDL.
def pandas_to_postgres(df):
dtype_dict = {
'i': 'integer',
'O': 'text',
'f': 'real',
'b': 'boolean',
'datetime64[ns]': 'timestamp',
'datetime64[ns, UTC]': 'timestampz',
}
column_list = []
column_dict = {}
for k, v in df.dtypes.items():
column_dict['name'] = k
column_dict['dtype'] = dtype_dict.get(v.kind, 'text')
column_list.append(column_dict.copy())
return column_list
def generate_create_table(df, schema, table, table_type, columns, constraint, unique_columns):
""" Returns a dictionary of coefs from training """
query = Template(
template
).render(
schema_name=schema,
table_name=table,
table_type=table_type,
columns=columns,
constraint=constraint,
constraint_columns=unique_columns
)
print(query)
Сегодня этот выпуск поддерживает SQLAthanor (v.0.3.0). Используя SQLAthanor, вы можете программно генерировать объект Table
SQLAlchemy со следующим кодом (при условии, что metadata
содержат объект SQLAlchemy MetaData
):
from sqlathanor import Table
my_table = Table.from_yaml('yaml_file.yaml',
'my_table_name',
metadata,
primary_key = 'id')
ETA: Обратите внимание, что вы также можете создавать объекты Table
используя Table.from_json()
, Table.from_dict()
и Table.from_csv()
.
Здесь документация (как правило) о том, как она работает: https://sqlathanor.readthedocs.io/en/latest/using.html#generating-sqlalchemy-tables-from-serialized-data
И здесь ссылка на документацию конкретного Table.from_yaml()
: https://sqlathanor.readthedocs.io/en/latest/api.html#sqlathanor.schema.Table.from_yaml
(Я рекомендую просмотреть документацию по методу - он переходит в некоторые из "готовых" программных построений объекта Table
из сериализованных данных)
ETA:
В принципе, способ создания программных Table
заключается в том, что SQLAthanor:
Сначала преобразует сериализованную строку (или файл) в Python dict
. Для YAML стандартным де-сериализатором является PyYAML. Для JSON, по умолчанию де-сериализатору является simplejson (оба по умолчанию deserializers может быть переопределен с помощью deserialize_function
аргумента).
После того, как Python dict
сгенерирован, SQLAthanor читает каждый из ключей в этом dict
, чтобы определить имена столбцов. Он считывает значения для каждого ключа и на основе типа данных значения пытается "угадать" в типе данных SQLAlchemy.
Учитывая то, что он нашел на шаге 2, он создает объект Table
с объектами Column
где каждый объект Column
соответствует ключу де-сериализованного dict
.
Если вам нужен более точный контроль над каждым Column
, вы можете:
type_mapping
аргумента (type_mapping
получает dict
, где клавиши верхнего уровня соответствуют имени столбца, и каждое значение является типом данных, чтобы применить к Column
)Column
конструктору с использованием column_kwargs
аргумента (column_kwargs
получает dict
, где клавиши верхнего уровня соответствует имени столбца, и каждое значение является dict
с аргументами ключевых слов, которые будут поставлены этим конструктора столбец. По умолчанию Table.from_<format>()
не поддерживает вложенные структуры данных. По умолчанию для skip_nested
установлено значение True
, что означает, что ключ в де-сериализованном dict
который содержит вложенный объект (или итерируемый или dict
), будет пропущен (т.е. не получит соответствующий Column
). Если вашей Table
необходимо хранить вложенные данные, вы можете установить skip_nested
в False
и активировать default_to_str
в True
. Это приведет к преобразованию вложенных данных (итераций или объектов dict
) в строки и, таким образом, сохранит их в столбце " Text
(если не переопределено type_mapping
).
Table.from_dict()
Ниже приведен пример dict
который может быть поставлен в Table.from_dict()
:
sample_dict = {
'id': 123,
'some_column_name': 'Some Column Value',
'created_datetime': datetime.utcnow()
}
my_table = Table.from_dict(sample_dict,
'my_table',
metadata,
primary_key = 'id')
Когда подается в Table.from_dict()
это dict
будет производить Table
объект с именем таблицы базы данных my_table
, который содержит три колонки:
id
который будет иметь тип Integer
который установлен как первичный ключ таблицыsome_column_name
которое будет иметь тип Text
created_datetime
которое будет иметь тип DateTime
Table.from_yaml()
Ниже приведен один и тот же пример, но вместо этого используется строка/документ YAML, которая может быть Table.from_yaml()
в Table.from_yaml()
:
sample_yaml = """
id: 123
some_column_name: Test Value
created_timestamp: 2018-01-01T01:23:45.67890
"""
my_table = Table.from_yaml(sample_yaml,
'my_table',
metadata,
primary_key = 'id')
Когда он будет Table.from_yaml()
в Table.from_yaml()
он сначала де-сериализует sample_yaml
в dict
же, как в предыдущем примере, а затем создает объект Table
с именем таблицы базы данных my_table
который содержит три столбца:
id
который будет иметь тип Integer
который установлен как первичный ключ таблицыsome_column_name
которое будет иметь тип Text
created_datetime
которое будет иметь тип DateTime
Надеюсь это поможет!
Table.from_dict()
иTable.from_yaml()
с Pythondict
и соответствующим документом YAML. Надеюсь, это поможет уточнить!