kurtgn
kurtgn

Reputation: 8710

pytest django: unable to access db in fixture teardown

I need to explicitly delete a fixture after it is used. I know that pytest-django by default drops all objects on teardown but in this particular case I need to do it manually. However, although my tests are marked as pytest.mark.django_db, I am able to create a fixture, but unable to delete it after a yield line:

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)

this is my test output:

============================= 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.

Why is this? and how do I enable DB assess on fixture teardown?

Upvotes: 5

Views: 3355

Answers (2)

Tom
Tom

Reputation: 2016

The db fixture as mentioned by Andreas Profous is function scoped so that wouldn't work here.

What you need to do is:

@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 ensures the test database is setup (and ready) for additional records to be added. django_db_blocker is what the db fixture uses to allow function scoped modifications. The reason why it's function scoped is so that it will behave how TestCase methods behave in the default unittest framework where the records are rolled back after each test. This is a good thing, it ensures a modification of a record in one test won't change the outcome in other test.

So be very careful when creating fixtures that aren't function scoped as your modifications won't by default be wrapped in a transaction and rolled back. Using the django_db_blocker gives you hooks into the django_db_blocker.unblock() method which removes the block from modifying the database. This is required in your non-function scoped fixtures.

Upvotes: 6

Andreas Profous
Andreas Profous

Reputation: 1512

You need to require the db fixture in some_fixture:

def some_fixture(db): ...

Upvotes: 0

Related Questions