【问题标题】:Using sqlalchemy-continuum with flask-sqlalchemy and flask-migrate将 sqlalchemy-continuum 与 flask-sqlalchemy 和 flask-migrate 一起使用
【发布时间】:2018-06-27 14:35:12
【问题描述】:

我正在尝试让 sqlalchemy-continuum 与 flask-sqlalchemy 和 flask-migrate 一起工作。我的__init__.py 文件如下所示:

import os

from flask import Flask

def create_app():
    """Create and configure an instance of the Flask application."""
    app = Flask(__name__, instance_relative_config=True)

    app.config.from_mapping(
        SQLALCHEMY_DATABASE_URI='postgres+psycopg2://{}:{}@{}:{}/{}'.format(
            os.environ['POSTGRES_USER'],
            os.environ['POSTGRES_PASSWORD'],
            os.environ['POSTGRES_HOST'],
            os.environ['POSTGRES_PORT'],
            os.environ['POSTGRES_DB']
        ),
        SQLALCHEMY_TRACK_MODIFICATIONS=False
    )

    try:
        os.makedirs(app.instance_path)
    except OSError:
        pass

    from .models import db, migrate
    db.init_app(app)
    migrate.init_app(app, db)

    return app

我的 models.py 文件如下所示:

import sqlalchemy
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from sqlalchemy_continuum import make_versioned


db = SQLAlchemy()
migrate = Migrate()

make_versioned(user_cls=None)

class User(db.Model):
    __versioned__ = {}
    __tablename__ = 'user'

    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True, nullable=False)
    email = db.Column(db.String(120), unique=True, nullable=False)
    password = db.Column(db.String(20), unique=True, nullable=False)

    def __repr__(self):
        return '<User {} - {}>'.format(self.username, self.email)

sqlalchemy.orm.configure_mappers()

然后我运行以下 flask-migrate 命令来初始化和迁移数据库:

flask db init
flask db migrate
flask db upgrade

flask db upgrade 命令的输出似乎显示了正在创建的正确表:

INFO  [alembic.runtime.migration] Context impl PostgresqlImpl.
INFO  [alembic.runtime.migration] Will assume transactional DDL.
INFO  [alembic.autogenerate.compare] Detected added table 'transaction'
INFO  [alembic.autogenerate.compare] Detected added table 'user'
INFO  [alembic.autogenerate.compare] Detected added table 'user_version'
INFO  [alembic.autogenerate.compare] Detected added index 'ix_user_version_end_transaction_id' on '['end_transaction_id']'
INFO  [alembic.autogenerate.compare] Detected added index 'ix_user_version_operation_type' on '['operation_type']'
INFO  [alembic.autogenerate.compare] Detected added index 'ix_user_version_transaction_id' on '['transaction_id']'

在 python shell 中,我可以执行以下操作:

>>> from test_flask.__init__ import create_app
>>> from test_flask.models import db, User

>>> app = create_app()
>>> with app.app_context():
...     user = User(username='devuser', email='devuser@gmail.com', 
password='devpassword')
...     db.session.add(user)
...     db.session.commit()

这似乎工作正常,但是当我尝试使用以下方式访问版本属性中的元素时:

>>> user.versions[0]

我收到以下错误:

Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/local/lib/python3.6/site-packages/sqlalchemy/orm/dynamic.py", line 254, in __getitem__
  attributes.PASSIVE_NO_INITIALIZE).indexed(index)
File "/usr/local/lib/python3.6/site-packages/sqlalchemy/orm/dynamic.py", line 359, in indexed
  return list(self.added_items)[index]
IndexError: list index out of range

命令:

>>> user.versions

返回:

<sqlalchemy.orm.dynamic.AppenderQuery object at 0x7f6515d3a898>

这似乎不是 sqlalchemy-continuum docs 中指定的版本属性的预期行为。关于我做错了什么有什么想法吗?

【问题讨论】:

  • 我不熟悉 sqlalchemy-continuum 扩展。在这种情况下,user.versions 的期望值是多少?
  • @Miguel 我希望user.versions 是一个可迭代的,它存储我的用户实例的先前版本。所以user.versions[0] 应该是我的用户实例的当前版本,因为自创建以来我没有对其进行任何更改
  • 您在问题中显示的 AppenderQuery 类是可迭代的。我的问题是这是否是该对象的预期类型。当连续扩展正常工作时,user.versions 打印什么?

标签: python flask flask-sqlalchemy sqlalchemy-continuum


【解决方案1】:

这个错误可能在两种情况下发生,至少:

  • UPDATEINSERT 之后你没有提交
  • 或者你不在app上下文中

在您的烧瓶外壳示例中,所有语句都应位于上下文块内:

>>> with app.app_context():
...     user = User(username='devuser2', email='devuser2@gmail.com', password='devpassword2')
...     db.session.add(user)
...     db.session.commit()
...     user.versions[0].username
...     user.username='devuser_fixed'
...     db.session.commit()    
...     user.versions[1].username

#'devuser'
#'devuser_fixed'

在上下文之外,user 仍然存在,变量留在内存中,但是连接到数据库的会话丢失了。


注意:如果第二次提交丢失,user.versions[1] 将仅在实际上下文会话中可用。所以如果你退出这个上下文并检查user.versions[1],你将面临同样的错误。

【讨论】:

    猜你喜欢
    • 2019-07-13
    • 1970-01-01
    • 2019-05-28
    • 2015-01-05
    • 2014-08-16
    • 2015-03-25
    • 2013-10-07
    • 2020-03-13
    • 2011-06-01
    相关资源
    最近更新 更多