我开始将 Alembic 合并到我已经使用 SQLAlchemy 表定义的项目中。目前我的数据库架构在我的应用程序外部进行管理,我想将整个架构放入我的表定义文件中。
在 PostgreSQL 中,我使用自定义域来存储电子邮件地址。PostgreSQL DDL 是:
CREATE DOMAIN email_address TEXT CHECK (value ~ '.+@.+')
如何在 SQLAlchemy 中表示该域的创建以及将其用作列数据类型?
我开始将 Alembic 合并到我已经使用 SQLAlchemy 表定义的项目中。目前我的数据库架构在我的应用程序外部进行管理,我想将整个架构放入我的表定义文件中。
在 PostgreSQL 中,我使用自定义域来存储电子邮件地址。PostgreSQL DDL 是:
CREATE DOMAIN email_address TEXT CHECK (value ~ '.+@.+')
如何在 SQLAlchemy 中表示该域的创建以及将其用作列数据类型?
使用像 SQLAlchemy 这样的东西的原因之一是数据库独立性(除了 ORM 的东西)。
但是,使用像这样通常非常特定于 DB 的低级构造使“DB 独立性”成为非论据,因此我会选择op.execute
在您的 alembic 迁移中编写一个简单的。
这通常是一个非常可接受的折衷方案,因为它使源代码更简单并且更不容易出错。
如果您依赖仅在一个数据库后端中可用的数据库功能(另一个示例可能来自 PostgreSQLltree
或hstore
来自 PostgreSQL),那么我认为使用迁移也不会出现任何问题,该迁移也仅适用于该目标后端 -结尾。
所以你可以这样做:
def upgrade():
op.execute("CREATE DOMAIN ...")
def downgrade():
op.execute("DROP DOMAIN ...")
另一方面,如果您计划支持不同的后端,这将行不通。
这可能远非可行的解决方案,但我认为最好的方法是 subclass sqlalchemy.schema._CreateDropBase
。
from sqlalchemy.schema import _CreateDropBase
class CreateDomain(_CreateDropBase):
'''Represent a CREATE DOMAIN statement.'''
__visit_name__ = 'create_domain'
def __init__(self, element, bind=None, **kw):
super(CreateDomain, self).__init__(element, bind=bind, **kw)
class DropDomain(_CreateDropBase):
'''Represent a DROP BASE statement.'''
__visit_name__ = 'drop_domain'
def __init__(self, element, bind=None, **kw):
super(DropDomain, self).__init__(element, bind=bind, **kw)
@compiles(CreateDomain, 'postgresql')
def visit_create_domain(element, compiler, **kw):
text = '\nCREATE DOMAIN %s AS %s' % (
compiler.prepare.format_column(element.name),
compiler.preparer.format_column(element.type_)) # doesn't account for arrays and such I don't think
default = compiler.get_column_default_string(column)
if default is not None:
text += " DEFAULT %s" % default
return text
显然,这是不完整的,但如果你非常想要这个,它应该给你一个很好的起点。:)