使用绑定在多个数据库中回滚Flask-SQLAlchemy事务

时间:2019-02-21 21:03:40

标签: python flask sqlalchemy flask-sqlalchemy

我正在构建一个使用多个数据库的Flask应用程序,因此我使用SQLAlchemy绑定。

由于我想隔离所有测试,因此我创建了session固定装置,该固定装置开始交易并为每个测试创建scoped_session。测试完成后,我将回滚应用于事务以再次获得初始状态。

当我只有一个数据库时,这很好用。但是,它不适用于新的绑定。在下面的示例中,我有三个测试:

  1. 测试一个创建2个User对象(默认绑定)。
  2. 测试二创建2个Message对象(messages绑定)。
  3. 测试三创建1 User和1 Message

回滚后,我打印每个表中的行数。 User表(默认绑定)始终被清除,但是Messagemessages绑定)从不删除先前的行。

如何删除所有绑定的行?

import pytest

from flask import Flask
from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()


class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80))

    def __repr__(self):
        return '<User %r>' % self.username


class Message(db.Model):
    __bind_key__ = 'messages'
    id = db.Column(db.Integer, primary_key=True)
    message = db.Column(db.String(80))

    def __repr__(self):
        return '<MyBindTable %r>' % self.message


def create_app():
    app = Flask(__name__)
    app.config[
        'SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + '/tmp/users.db'
    app.config['SQLALCHEMY_BINDS'] = {
        'messages': 'sqlite:///' + '/tmp/messages.db'
    }

    return app


@pytest.fixture(scope='session')
def test_app(request):
    app = create_app()

    ctx = app.app_context()
    ctx.push()

    def teardown():
        ctx.pop()

    request.addfinalizer(teardown)
    return app


@pytest.fixture(scope='session')
def db_fixture(test_app, request):
    db.init_app(test_app)
    db.create_all()

    def teardown():
        db.drop_all()

    request.addfinalizer(teardown)
    return db


@pytest.fixture(scope='function')
def session(db_fixture, request):
    connection = db_fixture.engine.connect()
    transaction = connection.begin()

    options = dict(bind=connection, binds={})
    session = db_fixture.create_scoped_session(options=options)

    db_fixture.session = session

    def teardown():
        transaction.rollback()
        print()
        print('Users:', len(User.query.all()))
        print('Messages:', len(Message.query.all()))
        connection.close()
        session.remove()

    request.addfinalizer(teardown)
    return session


def test_two_users(session):
    admin = User(username='user1')
    guest = User(username='user2')

    session.add(admin)
    session.add(guest)
    session.commit()

    users = User.query.all()

    assert len(users) == 2


def test_two_my_bind_table(session):
    message1 = Message(message='message1')
    message2 = Message(message='message2')

    session.add(message1)
    session.add(message2)
    session.commit()

    messages = Message.query.all()

    assert len(messages) == 2


def test_both_tables(session):
    admin = User(username='user3')
    row_bind = Message(message='message2')

    session.add(admin)
    session.add(row_bind)
    session.commit()

    messages = Message.query.all()
    users = User.query.all()

    assert len(users) == 1
    assert len(messages) == 1  # <-- It fails: len(messages) is 3

# OUTPUT:
# test.py::test_two_users PASSED
# Users: 0
# Messages: 0
#
# test.py::test_two_my_bind_table PASSED
# Users: 0
# Messages: 2  # <-- This should be 0
#
# test.py::test_both_tables FAILED
# Users: 0
# Messages: 3  # <-- This should be 0

0 个答案:

没有答案