2010-01-20 4 views

답변

5

SQLAlchemy 자체는 스키마의 자동 업데이트를 지원하지 않지만 마이그레이션을 자동화하는 타사 SQLAlchemy Migrate 도구가 있습니다. 그것이 작동하는 방법을 보려면 "Database schema versioning workflow" chapter을보십시오.

+0

안녕하십니까, 많은 생각 ... – jagguli

+0

새 링크를 요청할 수 있습니까? 그 사람은 죽은 것처럼 보입니다. 미리 감사드립니다. – Nonsingular

9

때때로 마이그레이션은 너무 많은 작업입니다. 변경된 코드를 실행할 때 열을 자동으로 추가하려고합니다. 그래서 여기에 그 기능이 있습니다.

주의 사항 : 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)) 
+0

대단히 감사합니다! 정확히 내가 무엇을 찾고 있었는지. – kay

0
# database.py has definition for engine. 
# from sqlalchemy import create_engine 
# engine = create_engine('mysql://......', convert_unicode=True) 

from database import engine 
from sqlalchemy import DDL 

add_column = DDL('ALTER TABLE USERS ADD COLUMN city VARCHAR(60) AFTER email') 
engine.execute(add_column) 
관련 문제