2011-10-05 11 views
5

Próbuję zmienić nazwę kolumny. Pierwsza próba była z tego skryptu:Jak napisać zmianę migracji nazw kolumn za pomocą 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') 

Running migrate.py test na mojej bazy danych dev (sqlite) działa tak jak i modernizacji i obniżki. Ale kiedy wdrażam go do mojego środowiska testowego na Heroku (gdzie używa się PostgreSQL 8.3), dostaję ślad, gdy próbuję dokonać aktualizacji. Gist to ta wiadomość:

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

Następnie spróbowałem użyć users.c.user_id w metodzie aktualizacji. Że nie w obu środowiskach .:

AttributeError: user_id 

Obejście używam teraz jest ten skrypt:

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') 

To już zalecane praktyki skopiować i wkleić model do skryptów sqlalchemy-migrować. Ale dodatkowe duplikacje są dla mnie trochę za duże. Każdy wie, jak to zrobić. Zakładając, że jest to błąd, chciałbym sugestie, w jaki sposób DRY up to obejście niektórych.

Odpowiedz

12

Okazuje się, że istnieje nawet SUCHY: er rozwiązanie tego, niż się spodziewałem. Introspekcja! Podobnie jak:

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') 

Działa jak urok!

+2

Przyjemnie, chociaż nie podoba mi się używanie autoload = True w migracjach schematu. Tylko w heads-upie do przyszłości, pamiętaj, aby zastosować zmiany w odwrotnej kolejności w downgrade! Jeśli tego nie zrobisz, (prawdopodobnie) dostaniesz mnóstwo błędów! –

1

Założę się, że nie można wygenerować żadnego SQL, ponieważ twoje referencje metadanych się mieszają. Wydaje się, że używasz dwóch różnych obiektów metadanych w swoich klasach Table i to naprawdę nie jest dobre. Potrzebujesz tylko jednego. Metadane śledzi nieaktualność obiektów, niezależnie od tego, czy musi wydawać zapytania o aktualizacje obiektów, ograniczenia klucza obcego itp., I musi znać wszystkie tabele i relacje.

Zmiana na użycie pojedynczego obiektu MetaData i przekazanie echo=True do połączenia sqlalchemy.create_engine, a następnie wydrukuje zapytanie SQL, którego używa do standardowego wyjścia. Spróbuj wykonać to zapytanie samodzielnie, gdy zalogujesz się jako ta sama rola (użytkownik) do PostgreSQL. Może się okazać, że jest to prosty problem z uprawnieniami.

Jeśli chodzi o wklejanie kopii: myślę, że Django ma dobrą konwencję umieszczania Table i klas deklaratywnych w swoim module i importowania ich. Jednak, ponieważ musisz przekazać obiekt MetaData do fabryki Table, to komplikuje sprawę. Możesz użyć pojedynczego/globalnego obiektu metadanych lub po prostu przekonwertować na deklaratywny.

Przez chwilę wybrałem implementację funkcji jednoargumentowych, które zwróciły Table obiektów, którym podano metadane i zapisano wynik w pamięci podręcznej - w efekcie implementując pojedynczą klasę modelu. Wtedy zdecydowałem, że było głupio i przestawiłem się na deklaratywny.

+0

Zdaję sobie sprawę, że nie jest dobra praktyka, aby korzystać z dwóch obiektów metadanych. Ale to tylko obejście, aby skrypt działał. To pierwszy skrypt, który nie działa. Śledzenie zawiera instrukcję SQL i próbuje zmienić kolumnę według niewłaściwej nazwy. Dobrze wiedzieć o tym echo = Prawda, przyda się! – PEZ

+0

Informacje o kopiowaniu bez kopiowania. Jest to rada z przewodnika USer w serwisie sqlalchemy: http://packages.python.org/sqlalchemy-migrate/versioning.html # writing-scripts-with-consistent-behaviour Nie wymyśliłem jak używać deklaratywnego razem z sqlalchemy-migrate. Czy to możliwe? – PEZ

+0

Nie próbowałem tego. Powodzenia! – wberry

2

Ten działa również:

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') 
Powiązane problemy