Python Alembic:如何迁移模型中的自定义类型?
我的Python Alembic:如何迁移模型中的自定义类型?,python,sqlalchemy,flask-sqlalchemy,alembic,Python,Sqlalchemy,Flask Sqlalchemy,Alembic,我的用户型号为 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, nul
用户
型号为
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())
其中GUID
是中所述的自定义类型(完全相同)
现在当我跑的时候
alembic revision --autogenerate -m "Added initial table"
我的升级()
如下
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 ###
但在应用升级->alembic升级头时,我看到
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'
如何在这里使用GUID
/自定义类型?您可以根据方言将sa.GUID()
替换为sa.CHAR(32)
或UUID()
(从sqlalchemy.dialogs.postgresql import UUID添加导入行后)
将其替换为GUID()
(从.models.custom\u types import GUID中添加导入行)也可以,但是升级脚本绑定到模型代码,这可能不是一件好事。我遇到了类似的问题,并按如下方式解决了它:
假设您有以下模块my_guid
,其中包含(从您已经引用的页面中,经过少量命名修改):
如果在模型中使用此GUID,则只需在alembic/env.py
处添加三行:
from my_guid import GUID
import sqlalchemy as sa
sa.GUID = GUID
这对我很管用。希望有帮助 对于大多数自定义类型,使用impl
属性类的\uuuuuu repr\uuuu
函数对我很有效。我发现将迁移定义包含在类中,而不用担心将导入放入env.py
或scripts.py.mako
,这样会更干净。此外,它还可以轻松地在模块之间移动代码
Class GUID(types.TypeDecorator)
impl = CHAR
def __repr__(self):
return self.impl.__repr__()
# You type logic here.
自动迁移将生成CHAR(length=XXX)
我的解决方案使用sqlalchemy\u utils.types.uuid.UUIDType
,如果您在没有uuid
类型的数据库上,则使用CHAR(32)
或BINARY(16)
来表示uuid。您需要在迁移中考虑到这一点,迁移必须在没有UUID
类型的数据库上创建CHAR(32)/BINARY(16)
,在有它的数据库上创建UUIDType
我的SQLAlchemy类如下所示:
来自sqlalchemy\u utils.types.uuid导入UUIDType
从sqlalchemy导入字符、列、整数
Base=声明性_Base()
def get_uuid():
返回str(uuid.uuid4())
类仪表板(基本):
__tablename_uuu=‘仪表盘’
id=列(整数,主键=True)
uuid=列(UUIDType(binary=False),默认值=get\u uuid)
实际的批处理操作如下所示(支持SQLite、MySQL和Postgres):
from superset import db#设置SQLAlchemy连接
def升级():
bind=op.get_bind()
session=db.session(bind=bind)
db_type=session.bind.dialogue.name
def add_uuid_列(列名称,类型):
“”“将uuid列添加到给定表”“”
将op.batch\u alter\u表(col\u name)作为批处理操作:
批处理操作添加列(列('uuid',UUIDType(二进制=False),默认=get\uuid))
对于会话中的查询(\u类型):
s、 uuid=get_uuid()
会话。合并(个)
如果db_类型!='postgresql':
将op.batch\u alter\u表(col\u name)作为批处理操作:
批处理操作更改列('uuid',现有类型=字符(32),
新列(名称='uuid',可空=假)
批处理操作创建唯一约束('uq_uuid',['uuid'])
session.commit()
添加_uuid_列(“仪表板”,仪表板)
session.close()
希望这有帮助 但是如果我使用sqlite
的话,这会破坏测试,而且它不会是一致的,不是吗?我真的不太喜欢在测试中使用sqlite
,因为测试环境应该紧跟产品。除此之外,您是否针对多种方言部署了相同的代码?无论如何,我希望迁移脚本在某个时间点与实际模式保持一致,与代码的一致性受到谴责,因为代码永远在变化。我想我会关心sqlite用于我的测试,因为它们可以在内存中运行,但是你说的话也很有道理,让我试试你说的,然后回答你。我完全同意你的观点,并且有PostgreSQL用于dev
和test
环境,你的回答对我有很大帮助@sayap。谢谢你为我做了很多工作。谢谢。第二,这对我来说很有效,而关于这个和其他问题的许多其他答案都不起作用。谢谢我不认为这一点单独起作用,至少在Alembic 1.4.3和SQLAlchemy ORM中是这样。假设您的自定义类型是在app.models
中定义或导入的。生成的迁移将只包含app.models.CHAR
,而不是app.models.GUID
。可能在回答此问题后默认渲染已更改?
Class GUID(types.TypeDecorator)
impl = CHAR
def __repr__(self):
return self.impl.__repr__()
# You type logic here.