Skip to content

Fix "db access not allowed" with both transactional_db and db fixtures #127

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
Changelog
=========

NEXT
----

* Fix interaction between ``db`` and ``transaction_db`` fixtures (#126).

2.6.2
-----

Expand Down
74 changes: 41 additions & 33 deletions pytest_django/fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,39 @@ def teardown_database():
if not request.config.getvalue('reuse_db'):
request.addfinalizer(teardown_database)

def _django_db_fixture_helper(transactional, request, _django_cursor_wrapper):
if is_django_unittest(request.node):
return

if transactional:
_django_cursor_wrapper.enable()

def flushdb():
"""Flush the database and close database connections"""
# Django does this by default *before* each test
# instead of after.
from django.db import connections
from django.core.management import call_command

for db in connections:
call_command('flush', verbosity=0,
interactive=False, database=db)
for conn in connections.all():
conn.close()

request.addfinalizer(_django_cursor_wrapper.disable)
request.addfinalizer(flushdb)
else:
if 'live_server' in request.funcargnames:
return
from django.test import TestCase

_django_cursor_wrapper.enable()
_django_cursor_wrapper._is_transactional = False
case = TestCase(methodName='__init__')
case._pre_setup()
request.addfinalizer(_django_cursor_wrapper.disable)
request.addfinalizer(case._post_teardown)

################ User visible fixtures ################

Expand All @@ -70,24 +103,16 @@ def db(request, _django_db_setup, _django_cursor_wrapper):
This database will be setup with the default fixtures and will
have the transaction management disabled. At the end of the test
the transaction will be rolled back to undo any changes to the
database. This is more limited then the ``transaction_db``
database. This is more limited then the ``transactional_db``
resource but faster.

If both this and ``transaction_db`` are requested then the
database setup will behave as only ``transaction_db`` was
If both this and ``transactional_db`` are requested then the
database setup will behave as only ``transactional_db`` was
requested.
"""
if ('transactional_db' not in request.funcargnames and
'live_server' not in request.funcargnames and
not is_django_unittest(request.node)):

from django.test import TestCase

_django_cursor_wrapper.enable()
case = TestCase(methodName='__init__')
case._pre_setup()
request.addfinalizer(_django_cursor_wrapper.disable)
request.addfinalizer(case._post_teardown)
if 'transactional_db' in request.funcargnames:
return request.getfuncargvalue('transactional_db')
return _django_db_fixture_helper(False, request, _django_cursor_wrapper)


@pytest.fixture(scope='function')
Expand All @@ -99,27 +124,10 @@ def transactional_db(request, _django_db_setup, _django_cursor_wrapper):

If you want to use the database with transactions you must request
this resource. If both this and ``db`` are requested then the
database setup will behave as only ``transaction_db`` was
database setup will behave as only ``transactional_db`` was
requested.
"""
if not is_django_unittest(request.node):
_django_cursor_wrapper.enable()

def flushdb():
"""Flush the database and close database connections"""
# Django does this by default *before* each test
# instead of after.
from django.db import connections
from django.core.management import call_command

for db in connections:
call_command('flush', verbosity=0,
interactive=False, database=db)
for conn in connections.all():
conn.close()

request.addfinalizer(_django_cursor_wrapper.disable)
request.addfinalizer(flushdb)
return _django_db_fixture_helper(True, request, _django_cursor_wrapper)


@pytest.fixture()
Expand Down
26 changes: 24 additions & 2 deletions tests/test_database.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@


def noop_transactions():
"""Test whether transactions are disabled
"""Test whether transactions are disabled.

Return True if transactions are disabled, False if they are
enabled.
"""

# Newer versions of Django simply runs standard tests in an atomic block.
# Newer versions of Django simply run standard tests in an atomic block.
if hasattr(connection, 'in_atomic_block'):
return connection.in_atomic_block
else:
Expand Down Expand Up @@ -109,6 +109,28 @@ def test_fin(self, fin):
pass


class TestDatabaseFixturesBothOrder:
@pytest.fixture
def fixture_with_db(self, db):
Item.objects.create(name='spam')

@pytest.fixture
def fixture_with_transdb(self, transactional_db):
Item.objects.create(name='spam')

def test_trans(self, fixture_with_transdb):
pass

def test_db(self, fixture_with_db):
pass

def test_db_trans(self, fixture_with_db, fixture_with_transdb):
pass

def test_trans_db(self, fixture_with_transdb, fixture_with_db):
pass


class TestDatabaseMarker:

@pytest.mark.django_db
Expand Down