2013-03-27 19 views
8

Moja User model jestAlembic: Jak przenieść niestandardowy typ do modelu?

class User(UserMixin, db.Model): 
    __tablename__ = 'users' 
    # noinspection PyShadowingBuiltins 
    uuid = Column('uuid', GUID(), default=uuid.uuid4, primary_key=True, 
        unique=True) 
    email = Column('email', String, nullable=False, unique=True) 
    _password = Column('password', String, nullable=False) 
    created_on = Column('created_on', sa.types.DateTime(timezone=True), 
         default=datetime.utcnow()) 
    last_login = Column('last_login', sa.types.DateTime(timezone=True), 
         onupdate=datetime.utcnow()) 

gdzie GUID jest niestandardowy typ opisany w sqlalchemy docs (dokładnie to samo)

Teraz gdy uruchamiam

alembic revision --autogenerate -m "Added initial table" 

dostanę upgrade() jak

def upgrade(): 
    ### commands auto generated by Alembic - please adjust! ### 
    op.create_table('users', 
    sa.Column('uuid', sa.GUID(), nullable=False), 
    sa.Column('email', sa.String(), nullable=False), 
    sa.Column('password', sa.String(), nullable=False), 
    sa.Column('created_on', sa.DateTime(timezone=True), nullable=True), 
    sa.Column('last_login', sa.DateTime(timezone=True), nullable=True), 
    sa.PrimaryKeyConstraint('uuid'), 
    sa.UniqueConstraint('email'), 
    sa.UniqueConstraint('uuid') 
    ) 
    ### end Alembic commands ### 

ale podczas stosowania aktualizacji ->alembic upgrade head widzę

File "alembic/versions/49cc74d0da9d_added_initial_table.py", line 20, in upgrade 
    sa.Column('uuid', sa.GUID(), nullable=False), 
AttributeError: 'module' object has no attribute 'GUID' 

Jak mogę pracować z GUID/Typ niestandardowy tutaj?

Odpowiedz

7

Można zamienić sa.GUID() na sa.CHAR(32) lub UUID() (po dodaniu linii importu from sqlalchemy.dialects.postgresql import UUID) w zależności od dialektu.

Zastępuje ją także GUID() (po dodaniu linii importu from your.models.custom_types import GUID), ale skrypt aktualizacyjny jest powiązany z kodem modelu, co może nie być dobre.

+0

ale to włamałoby się do testów, jeśli używam 'sqlite' plus to nie będzie zgodne, prawda? – daydreamer

+1

Naprawdę nie przejmuję się używaniem "sqlite" w testach, ponieważ środowisko testowe powinno ściśle podążać za produkcją. Poza tym, wdrażasz ten sam kod przeciwko więcej niż jednemu dialektowi? – sayap

+0

W każdym razie chciałbym, aby skrypty migracji były spójne z rzeczywistym schematem w danym momencie, spójność z kodem jest przeklęta, ponieważ kod zmienia się na zawsze. – sayap

3

miałem podobny problem i rozwiązać go jak następuje:

Załóżmy, że masz taki moduł my_guid zawierający (od strony, którą już cytowanym, z niewielkimi modyfikacjami nazewnictwa):

import uuid as uuid_package 
from sqlalchemy.dialects.postgresql import UUID as PG_UUID 
from sqlalchemy import TypeDecorator, CHAR 

class GUID(TypeDecorator): 
    impl = CHAR 

    def load_dialect_impl(self, dialect): 
     if dialect.name == 'postgresql': 
      return dialect.type_descriptor(PG_UUID()) 
     else: 
      return dialect.type_descriptor(CHAR(32)) 

    def process_bind_param(self, value, dialect): 
     if value is None: 
      return value 
     elif dialect.name == 'postgresql': 
      return str(value) 
     else: 
      if not isinstance(value, uuid_package.UUID): 
       return "%.32x" % uuid_package.UUID(value) 
      else: 
       # hexstring 
       return "%.32x" % value 

    def process_result_value(self, value, dialect): 
     if value is None: 
      return value 
     else: 
      return uuid_package.UUID(value) 

Jeśli używasz tego GUID w swoich modelach, wystarczy dodać trzy linie na alembic/env.py:

from my_guid import GUID 
import sqlalchemy as sa 
sa.GUID = GUID 

który pracował dla mnie. Mam nadzieję, że pomaga!

+1

Pracowałem dla mnie. Dzięki. – Soferio

+0

Po drugie - działało to dla mnie, podczas gdy wiele innych odpowiedzi na to i inne pytania nie. Dzięki! –

1

Używanie klasy klasy atrybutów dla mnie, dla większości typów niestandardowych. Uważam, że definicja migracji zawarta w klasie jest bardziej czysta niż martwienie się o umieszczanie importu w twoim env.py lub scripts.py.mako. Dodatkowo ułatwia przenoszenie kodu między modułami.

Class GUID(types.TypeDecorator) 
    impl = CHAR 

    def __repr__(self): 
     return self.impl.__repr__() 

    # You type logic here. 

Automatyzacja wyprodukuje CHAR(length=XXX).

Powiązane problemy