首页 > 解决方案 > pytest django:无法在夹具拆解中访问数据库

问题描述

我需要在使用后显式删除一个夹具。我知道 pytest-django 默认情况下会在拆卸时丢弃所有对象,但在这种特殊情况下,我需要手动完成。然而,虽然我的测试被标记为pytest.mark.django_db,我可以创建一个夹具,但无法在yield一行之后删除它:

import pytest

from tgapps.models import TelegramApp


@pytest.fixture(scope='module')
def some_fixture():

    app = TelegramApp.objects.create(
        session_data=b'\xa2\x8f#',
        app_owner_phone=79856235474,
        app_id=182475,
        app_hash='aad9ab4384fea1af0342b77b606d13b0'
    )
    yield app

    print('deleting object...')
    app.delete()


class TestTelegramServiceObject(object):

    @pytest.mark.django_db
    def test1(self, some_fixture):
        print('Fixture created:')
        print(some_fixture)

这是我的测试输出:

============================= test session starts ==============================
platform darwin -- Python 3.6.4, pytest-3.4.0, py-1.5.2, pluggy-0.6.0
Django settings: inviter.settings.staging (from ini file)
rootdir: /Users/1111/_projects/fasttrack/inviter, inifile: pytest.ini
plugins: mock-1.7.1, dotenv-0.1.0, django-3.1.2
collected 1 item
test_example.py E.Fixture created:
<79856235474 - 182475>
deleting object...

tests/api/test_example.py:25 (TestTelegramServiceObject.test1)
@pytest.fixture(scope='module')
    def some_fixture():

        app = TelegramApp.objects.create(
            session_data=b'\xa2\x8f#',
            app_owner_phone=79856235474,
            app_id=182475,
            app_hash='aad9ab4384fea1af0342b77b606d13b0'
        )
        yield app

        print('deleting object...')
>       app.delete()

test_example.py:21: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/Users/1111/.virtualenvs/inviter-WB5rPISo/lib/python3.6/site-packages/django/db/models/base.py:890: in delete
    collector.collect([self], keep_parents=keep_parents)
/Users/1111/.virtualenvs/inviter-WB5rPISo/lib/python3.6/site-packages/django/db/models/deletion.py:221: in collect
    elif sub_objs:
/Users/1111/.virtualenvs/inviter-WB5rPISo/lib/python3.6/site-packages/django/db/models/query.py:276: in __bool__
    self._fetch_all()
/Users/1111/.virtualenvs/inviter-WB5rPISo/lib/python3.6/site-packages/django/db/models/query.py:1179: in _fetch_all
    self._result_cache = list(self._iterable_class(self))
/Users/1111/.virtualenvs/inviter-WB5rPISo/lib/python3.6/site-packages/django/db/models/query.py:53: in __iter__
    results = compiler.execute_sql(chunked_fetch=self.chunked_fetch, chunk_size=self.chunk_size)
/Users/1111/.virtualenvs/inviter-WB5rPISo/lib/python3.6/site-packages/django/db/models/sql/compiler.py:1062: in execute_sql
    cursor = self.connection.cursor()
/Users/1111/.virtualenvs/inviter-WB5rPISo/lib/python3.6/site-packages/django/db/backends/base/base.py:255: in cursor
    return self._cursor()
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <django.db.backends.postgresql.base.DatabaseWrapper object at 0x1048bf080>
name = None

    def _cursor(self, name=None):
>       self.ensure_connection()
E       Failed: Database access not allowed, use the "django_db" mark, or the "db" or "transactional_db" fixtures to enable it.

为什么是这样?以及如何在夹具拆卸时启用 DB 评估?

标签: djangopytestpytest-django

解决方案


Andreas Profous 提到的db夹具是功能范围的,因此在这里不起作用。

你需要做的是:

@pytest.fixture(scope='module')
def some_fixture(django_db_setup, django_db_blocker):
    with django_db_blocker.unblock():
        app = TelegramApp.objects.create(
            session_data=b'\xa2\x8f#',
            app_owner_phone=79856235474,
            app_id=182475,
            app_hash='aad9ab4384fea1af0342b77b606d13b0'
        )
    yield app
    with django_db_blocker.unblock():
        print('deleting object...')
        app.delete()

django_db_setup确保测试数据库已设置(并准备好)以添加其他记录。django_db_blockerdb夹具用来允许功能范围修改的东西。它是函数作用域的原因是它会表现出TestCase方法在默认 unittest 框架中的行为方式,其中记录在每次测试后回滚。这是一件好事,它确保在一个测试中修改记录不会改变其他测试的结果。

因此,在创建非功能范围的固定装置时要非常小心,因为默认情况下您的修改不会被包装在事务中并回滚。使用django_db_blocker可以挂钩到django_db_blocker.unblock()从修改数据库中删除块的方法。这在您的非功能范围固定装置中是必需的。


推荐阅读