Непонятное поведение SQLAlchemy;объект является или не присоединен к сеансу в зависимости от запроса test_client - PullRequest
0 голосов
/ 04 июня 2019

Я не понимаю, почему загруженный объект в SQLAlchemy не обновляется после обновления базы данных (и session.commit()) в одной версии теста, а не в другой. Ссылочный объект также все еще прикреплен к сеансу в рабочей версии, но имеет Session.object_session(object) == None в нерабочей версии. Я использую приспособления pytest для настройки среды сеансов и приложений, и у меня есть clean_* их версии, которые я использую, потому что существуют версии этих приспособлений, которые ожидают, что база данных также не будет пустой. Вот код (сначала исправления для контекста, затем рабочие и нерабочие тесты)

приборы:

@pytest.fixture(scope="session")
def postgresql(request, logger):
    p = testing.postgresql.Postgresql()
    logger.debug(p.url())

    def fin():
        logger.debug("teardown DB")
        p.stop()

    request.addfinalizer(fin)

    return p

@pytest.fixture()
def clean_app(request, clean_postgresql):

    test_config = TestConfig()
    test_config.SQLALCHEMY_DATABASE_URI = clean_postgresql.url()

    app = create_app(test_config, settings_override={
        "SQLALCHEMY_DATABASE_URI": clean_postgresql.url(),
        "TOKEN_SECRET_KEY": "secret"
    })
    ctx = app.app_context()
    ctx.push()
    app.db = db
    db.create_all()

    def fin():
        ctx.pop()

    request.addfinalizer(fin)
    return app


@pytest.fixture()
def clean_session(app, request):
    connection = app.db.engine.connect()
    transaction = connection.begin()
    options = dict(bind=connection, binds={})
    session = app.db.create_scoped_session(options=options)

    original_session = app.db.session
    app.db.session = session

    def fin():
        transaction.rollback()
        session.remove()
        app.db.session = original_session

    request.addfinalizer(fin)
    return session


@pytest.fixture()
def clean_client(clean_app):
    client = clean_app.test_client()

    yield client

@pytest.fixture()
def make_foo():
  """Factory to make a foo object for tests"""
  def _make_foo(params={}):
    foo_params = dict(FOO_DEFAULTS, **params)
    return Foo.create(foo_params)
  yield _make_user

тест с закомментированным рабочим примером (класс Foo имеет отношение один-ко-многим с классом Bar):

def test_example(request, clean_app, clean_session, clean_client, make_object):
  foo = make_foo()
  foo.create_new_bar()
  assert len(foo.bars) == 1 #True
  response = clean_client.put(
      "/api/foos/{1}?action=create_bar".format(foo.id),
      headers={'Authorization': 'Bearer <Valid Token>'},
      json={'barAttribute': 18},
      # data=json.dumps({'barAttribute': 18}),
      # content_type='application/json'
  )
  assert response.status == 200 #True
  print(Session.object_session(foo)) #This prints `None` with current code
  assert len(foo.bars) == 2 #This fails with 1 != 2 with current code

Если я закомментирую строку json= и раскомментирую две нижние строки, код сработает, а Session.object_session(foo) выведет объект sqlalchemy.orm.session.SignallingSession, как и ожидалось.

Я явно не совсем понимаю, где-нибудь используется сеанс SQLAlchemy.

...