python - query - Alambique: ¿Cómo migrar el tipo personalizado en un modelo?
sql alchemy migrate (3)
Mi modelo de User
es
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())
donde GUID
es un tipo personalizado como se describe en sqlalchemy docs (Exactamente igual)
Ahora cuando corro
alembic revision --autogenerate -m "Added initial table"
Obtengo mi upgrade()
como
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 ###
pero durante la aplicación de actualización -> alembic upgrade head
, veo
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''
¿Cómo puedo hacer que funcione con GUID
/ tipo personalizado aquí?
Puede reemplazar sa.GUID()
con sa.CHAR(32)
o UUID()
(luego de agregar la línea from sqlalchemy.dialects.postgresql import UUID
) dependiendo del dialecto.
Sustituirlo con GUID()
(después de agregar la línea from your.models.custom_types import GUID
) también funcionará, pero el script de actualización está vinculado al código de su modelo, lo que puede no ser una buena idea.
Tuve un problema similar y lo resolvió de la siguiente manera:
Supongamos que tiene el siguiente módulo my_guid
, que contiene (de la página que ya citó, con modificaciones menores de nombres):
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)
Si usa este GUID en sus modelos, solo tiene que agregar tres líneas en alembic/env.py
:
from my_guid import GUID
import sqlalchemy as sa
sa.GUID = GUID
Eso funcionó para mí. ¡Espero que ayude!
Usar la función __repr__
de la clase de atributo __repr__
me funcionó para la mayoría de los tipos personalizados. Me parece más limpio tener la definición de migración contenida dentro de la clase en lugar de preocuparse por poner importaciones en su env.py
o scripts.py.mako
. Además, hace que sea fácil mover su código entre módulos.
Class GUID(types.TypeDecorator)
impl = CHAR
def __repr__(self):
return self.impl.__repr__()
# You type logic here.
La automigración producirá CHAR(length=XXX)
.