python - SqlAlchemy 向类中添加新字段并在表中创建相应的列

标签 python sqlalchemy

我想向现有的映射类添加一个字段,我将如何自动更新 sql 表。如果将字段添加到类中,sqlalchemy 是否提供一种使用新列更新数据库的方法。

最佳答案

有时迁移工作量太大 - 您只想在运行更改后的代码时自动添加列。所以这里有一个函数可以做到这一点。

注意事项:它在 SQLAlchemy 的内部结构中四处游荡,并且每次 SQLAlchemy 进行重大修订时往往需要进行小的更改。 (可能有更好的方法来做到这一点——我不是 SQLAlchemy 专家)。它也不处理约束。

import logging
import re

import sqlalchemy
from sqlalchemy import MetaData, Table, exceptions
import sqlalchemy.engine.ddl

_new_sa_ddl = sqlalchemy.__version__.startswith('0.7')


def create_and_upgrade(engine, metadata):
    """For each table in metadata, if it is not in the database then create it. 
    If it is in the database then add any missing columns and warn about any columns
    whose spec has changed"""
    db_metadata = MetaData()
    db_metadata.bind = engine

    for model_table in metadata.sorted_tables:
        try:
            db_table = Table(model_table.name, db_metadata, autoload=True)
        except exceptions.NoSuchTableError:
            logging.info('Creating table %s' % model_table.name)
            model_table.create(bind=engine)
        else:
            if _new_sa_ddl:
                ddl_c = engine.dialect.ddl_compiler(engine.dialect, None)
            else:
                # 0.6
                ddl_c = engine.dialect.ddl_compiler(engine.dialect, db_table)
            # else:
                # 0.5
                # ddl_c = engine.dialect.schemagenerator(engine.dialect, engine.contextual_connect())

            logging.debug('Table %s already exists. Checking for missing columns' % model_table.name)

            model_columns = _column_names(model_table)
            db_columns = _column_names(db_table)

            to_create = model_columns - db_columns
            to_remove = db_columns - model_columns
            to_check = db_columns.intersection(model_columns)

            for c in to_create:
                model_column = getattr(model_table.c, c)
                logging.info('Adding column %s.%s' % (model_table.name, model_column.name))
                assert not model_column.constraints, \
                    'Arrrgh! I cannot automatically add columns with constraints to the database'\
                        'Please consider fixing me if you care!'
                model_col_spec = ddl_c.get_column_specification(model_column)
                sql = 'ALTER TABLE %s ADD %s' % (model_table.name, model_col_spec)
                engine.execute(sql)

            # It's difficult to reliably determine if the model has changed 
            # a column definition. E.g. the default precision of columns
            # is None, which means the database decides. Therefore when I look at the model
            # it may give the SQL for the column as INTEGER but when I look at the database
            # I have a definite precision, therefore the returned type is INTEGER(11)

            for c in to_check:
                model_column = model_table.c[c]
                db_column = db_table.c[c]
                x =  model_column == db_column

                logging.debug('Checking column %s.%s' % (model_table.name, model_column.name))
                model_col_spec = ddl_c.get_column_specification(model_column)
                db_col_spec = ddl_c.get_column_specification(db_column)

                model_col_spec = re.sub('[(][\d ,]+[)]', '', model_col_spec)
                db_col_spec = re.sub('[(][\d ,]+[)]', '', db_col_spec)
                db_col_spec = db_col_spec.replace('DECIMAL', 'NUMERIC')
                db_col_spec = db_col_spec.replace('TINYINT', 'BOOL')

                if model_col_spec != db_col_spec:
                    logging.warning('Column %s.%s has specification %r in the model but %r in the database' % 
                                       (model_table.name, model_column.name, model_col_spec, db_col_spec))

                if model_column.constraints or db_column.constraints:
                    # TODO, check constraints
                    logging.debug('Column constraints not checked. I am too dumb')

            for c in to_remove:
                model_column = getattr(db_table.c, c)
                logging.warning('Column %s.%s in the database is not in the model' % (model_table.name, model_column.name))


def _column_names(table):
    # Autoloaded columns return unicode column names - make sure we treat all are equal
    return set((unicode(i.name) for i in table.c)) 

关于python - SqlAlchemy 向类中添加新字段并在表中创建相应的列,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2103274/

相关文章:

Python 编译脚本给出错误 "Can' t 加载插件 : sqlalchemy. 方言:presto”

python - 使用 SQLAlchemy 和 Flask jsonify 返回 JSON 格式的连接表

python - 如何为 python eve-sqlalchemy 编写自定义路由?

子查询中的 SqlAlchemy 闭包

python - 为什么 isnumeric 不起作用?

python - 使用 pytest 动态参数化类级固定装置

javascript - 假设我在 Django 中有这个循环......我该如何显示它?

python - 类型错误 : Object of type 'datetime' is not JSON serializable (with serialize function)

Python请求包脚本错误

python - 如何在不保存的情况下在python窗口中显示图像/屏幕截图