【问题标题】:Flask-SQLAlchemy Lower Case Index - skipping functional, not supported by SQLAlchemy reflectionFlask-SQLAlchemy 小写索引 - 跳过功能,SQLAlchemy 反射不支持
【发布时间】:2017-06-28 10:37:23
【问题描述】:

首先。抱歉,如果这个问题已经得到解答,但我在任何地方都找不到答案。

我需要在 Flask-SQLAlchemy 对象上定义一个小写索引。

我遇到的问题是我需要将模型用户名和电子邮件字段存储为小写,以便我可以使用User.query.filter(func.lower(username) == func.lower(username)).first() 进行检查

到目前为止,我一直只是通过将这些字段插入为小写来处理这个问题,但在这个特定的例子中,我需要用户名来保留定义它的大小写。

我认为我在正确的轨道上,但遇到了一个我以前从未见过的问题。

class User(UserMixin, db.Model):
    __tablename__ = 'users'

    id = db.Column(db.Integer, primary_key=True)
    # Indexes for username and email are defined below User
    # They are functional lowercase indexes
    username = db.Column(db.String(32))
    email = db.Column(db.String(255))
    password_hash = db.Column(db.String(255))

    ...

db.Index('ix_users_username', func.lower(User.username), unique=True)
db.Index('ix_users_email', func.lower(User.email), unique=True)

我看不出这有什么问题。我猜func.lower() 要求索引的定义与我在其他搜索中可以分辨的内容分开。

现在问题来了,当我运行迁移时出现以下错误:

INFO  [alembic.runtime.migration] Context impl PostgresqlImpl.
INFO  [alembic.runtime.migration] Will assume transactional DDL.
INFO  [alembic.autogenerate.compare] Detected added table 'users'
.../lib/python3.4/site-packages/alembic/util/messaging.py:69: UserWarning: autogenerate skipping functional index ix_users_email; not supported by SQLAlchemy reflection
  warnings.warn(msg)
.../lib/python3.4/site-packages/alembic/util/messaging.py:69: UserWarning: autogenerate skipping functional index ix_users_username; not supported by SQLAlchemy reflection

我不是 100% 确定为什么反射不支持这一点。我期望在我的迁移中它会像以前一样添加它们,但它会被包裹在较低的位置。

我并不反对手动编写迁移(因为我假设它可能,但不是 100% 确定如何)但是谁能指出为什么这不能开箱即用?

提前致谢 乔

更新

我通过在迁移中添加以下行来解决这个问题。

op.create_index('ix_users_username', 'users', [sa.text('lower(username)')])
op.create_index('ix_users_email', 'users', [sa.text('lower(email)')])

【问题讨论】:

  • 您需要手动编写迁移。我猜它不受支持,因为很难解析函数索引的表达式。 SQLAlchemy 从内存表示中呈现 SQL;它不会反过来。
  • 谢谢@univerio。我以为会是这种情况,只是有点困惑,因为我以为它会更简单。
  • @univero。我已经修改了迁移,一切都很好。不过有一件事。我已经删除了索引的定义,因为 Alembic 经常抱怨它。我只是在列定义上使用默认值 (..., index=True, unique=True)。这是你会做的吗?
  • 没有。您的模型应该反映您的数据库模式所处的确切状态,以便您可以执行metadata.create_all() 来创建一个新的数据库。如果您想消除警告,可以使用include_object 排除您的索引。
  • 完美!这也是我得出的结论。这已经成功了,感谢您的链接。如果您想要回答的分数,请随意。我可以通过我添加迁移的确切方式向答案发送编辑。我认为这可能是有用的答案。

标签: python postgresql flask sqlalchemy alembic


【解决方案1】:

您需要使用 alembic 的 execute 方法手动添加 UNIQUE INDEX。将类似的内容添加到您的迁移脚本中。

from alembic import op
# ...

def upgrade():
    # ...
    op.execute(
        """ CREATE UNIQUE INDEX users_normalized_username
            ON users
            (lower(username))
        """
    )

def downgrade():
    # ...
    op.execute("DROP INDEX users_normalized_username")

您还可以添加ColumnProperty,以便访问username 的规范化表示。

from sqlalchemy import Column, String, func, orm

class User(Base):
    __tablename__ = 'users'

    username = Column(String(32))
    normalized_username = orm.column_property(func.lower(username))

    # ...

这不会在您的数据库中创建额外的列,但您可以对其进行查询和比较。

session.query(User).filter(User.normalized_username == func.lower(username)).first()

【讨论】:

    【解决方案2】:

    我发现了一个技巧,在你的模型文件中你没有添加一个新的索引,但是你修改了一个现有的索引。然后运行 ​​db migrate,它应该能够为你生​​成炼金术 python 代码。然后你只需要调整炼金术python代码将其重命名为新的索引名称。并再次修改模型文件以添加新索引并恢复之前对现有索引所做的修改。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-07-23
      • 1970-01-01
      • 2019-03-06
      • 1970-01-01
      • 2013-05-10
      • 2015-03-25
      相关资源
      最近更新 更多