2011-10-05 9 views
5

私は列名を変更しようとしています。最初の試みは、このスクリプトを使用していた:私のdevのデータベース(SQLiteの)上migrate.py testを実行sqlalchemy-migrateを使用して列名の移行を変更する方法

meta = MetaData() 

users = Table('users', meta, 
    Column('id', Integer, primary_key=True), 
    Column('name', String(50), unique=True), 
    Column('email', String(120), unique=True) 
    ) 

def upgrade(migrate_engine): 
    meta.bind = migrate_engine 
    users.c.id.alter(name='id') 

def downgrade(migrate_engine): 
    meta.bind = migrate_engine 
    users.c.id.alter(name='user_id') 

は動作しますので、アップグレードとダウングレードを行います。しかし、それをHerokuのテスト環境(PostgreSQL 8.3が使用されている)にデプロイするとき、アップグレードしようとするとトレースがあります。

sqlalchemy.exc.ProgrammingError: (ProgrammingError) column "id" does not exist 

次に、アップグレード方法でusers.c.user_idを使用しようとしました。それはすでにSQLAlchemyの-移行スクリプトをコピー&ペーストモデルに練習をお勧めします

meta_old = MetaData() 
meta_new = MetaData() 

users_old = Table('users', meta_old, 
    Column('user_id', Integer, primary_key=True), 
    Column('name', String(50), unique=True), 
    Column('email', String(120), unique=True) 
    ) 

users_new = Table('users', meta_new, 
    Column('id', Integer, primary_key=True), 
    Column('name', String(50), unique=True), 
    Column('email', String(120), unique=True) 
    ) 

def upgrade(migrate_engine): 
    meta_old.bind = migrate_engine 
    users_old.c.user_id.alter(name='id') 

def downgrade(migrate_engine): 
    meta_new.bind = migrate_engine 
    users_new.c.id.alter(name='user_id') 

:それは両方の環境:

AttributeError: user_id 

私が今使っている問題を回避するには失敗したことは、このスクリプトです。しかし、この余分な複製は、私にはあまりにも多すぎます。誰にどのようにこれを行う必要があります知っている。それがバグだと仮定して、回避策をいくつかDRYする方法を提案したいと思います。

答えて

12

私が期待していた以上のDRY:ERソリューションがあります。イントロスペクション!ように:

def upgrade(migrate_engine): 
    meta = MetaData(bind=migrate_engine) 
    users = Table('users', meta, autoload=True) 
    users.c.user_id.alter(name='id') 

def downgrade(migrate_engine): 
    meta = MetaData(bind=migrate_engine) 
    users = Table('users', meta, autoload=True) 
    users.c.id.alter(name='user_id') 

魅力的な作品です!

+2

いいえ、スキーマの移行でautoload = Trueを使用することには注意が必要です。将来を見据えて、ダウングレードで変更を逆に適用することを忘れないでください!あなたがしなければ、あなたは多分エラーを(おそらく)得るでしょう! –

1

メタデータの参照が混ざり合っているため、SQLを生成できないと思います。 Tableクラスでは2つの異なるメタデータオブジェクトを使用しているようですが、それは本当にうまくいきません。あなたは1つだけ必要です。メタデータは、オブジェクト更新、外部キー制約などの問合せを発行する必要があるかどうかに関わらず、オブジェクトの古さを追跡し、すべての表および関係について知る必要があります。

単一のMetaDataオブジェクトを使用するように変更し、sqlalchemy.create_engine呼び出しにecho=Trueを渡すと、標準出力に使用しているSQLクエリが出力されます。 Postgresと同じ役割(ユーザ)としてログインしている間に、そのクエリを自分自身で実行してみてください。これは単純な権限の問題であることがわかります。

コピー貼り付けについて:私はDjangoがTableと宣言的なクラスを独自のモジュールに配置し、それらをインポートする良い慣習を持っていると思います。しかし、MetaDataオブジェクトをTableファクトリに渡す必要があるため、問題が複雑になります。シングルトン/グローバルメタデータオブジェクトを使用することも、宣言型に変換することもできます。

しばらく私は、Tableというオブジェクトをメタデータとして与え、その結果をキャッシュした結果、シングルトンモデルクラスを実装した、1引数の関数を実装することにしました。それから私はそれが愚かであり、宣言的に切り替えたと決めました。

+0

私はそれが2つのメタデータオブジェクトを使用することをお勧めではありません実現します。しかし、これはスクリプトを動作させるための回避策に過ぎません。これは動作しない最初のスクリプトです。トレースにはSQL文が含まれており、間違った名前で列を変更しようとしています。そのエコーについて知っておくと良い=真、それは便利です! – PEZ

+0

コピー貼り付けしないことについて。これは、sqlalchemy-migrate USer Guide:http://packages.python.org/sqlalchemy-migrate/versioningからのアドバイスです。html#writing-scripts-with-consistent-behavior sqlalchemy-migrateと一緒に宣言型を使用する方法を理解していません。出来ますか? – PEZ

+0

私はそれを試していません。がんばろう! – wberry

2

この1つはまた、動作します:

from alembic import op 
.... 
def upgrade(migrate_engine): 
    op.alter_column('users', 'user_id', new_column_name='id') 

def downgrade(migrate_engine): 
    op.alter_column('users', 'id', new_column_name='user_id') 
関連する問題