SQLAlchemy-从Yaml或字典创建表?

时间:2018-07-26 21:50:10

标签: python sqlalchemy yaml

是否可以通过yaml文件中指定的字典创建动态表?我在yaml文件中定义了许多ETL配置,所以我很好奇是否也可以向其中添加表创建方面,因此我不必在单独的目录中修改单独的.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)

但是后来我转到让熊猫确定dtype,而不是在yaml文件中定义它们。这将使用jinja2模板创建sql,然后循环浏览所有数据源以创建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)

1 个答案:

答案 0 :(得分:1)

今天发布的 SQLAthanor (v.0.3.0)完全支持这一点。使用 SQLAthanor ,您可以使用以下代码以编程方式生成SQLAlchemy Table对象(假设metadata包含您的SQLAlchemy MetaData对象):

from sqlathanor import Table

my_table = Table.from_yaml('yaml_file.yaml', 
                           'my_table_name', 
                           metadata, 
                           primary_key = 'id')

ETA:注意,还可以使用TableTable.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:

  1. 首先将序列化的字符串(或文件)转换为Python dict。对于YAML,默认反序列化器为PyYAML。对于JSON,默认的反序列化器为simplejson(两个默认的反序列化器都可以使用deserialize_function参数覆盖)。

  2. 一旦生成Python dict,SQLAthanor就会读取该dict中的每个键以确定列名。它会读取每个键的值,并根据该值的数据类型尝试“猜测” SQLAlchemy数据类型。

  3. 给出在步骤2中发现的内容,它将创建一个Table对象的Column对象,其中每个Column对象都对应于反序列化{{1 }}。

如果您需要对每个dict进行更精确的控制,则可以:

  • 使用Column参数覆盖其SQLAlchemy数据类型(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的{​​{1}}对象,该对象包含三列:

  • Table,其类型为my_table,将被设置为表的主键
  • id,其类型为Integer
  • some_column_name,其类型为Text

created_datetime示例

以下是相同的示例,但是使用了可以提供给DateTime的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(),然后生成具有数据库表名称的sample_yaml对象dict包含三列:

  • Table,其类型为my_table,将被设置为表的主键
  • id,其类型为Integer
  • some_column_name,其类型为Text

希望这会有所帮助!