Я получаю TransactionManagementError при попытке сохранить экземпляр модели Django User, а в сигнале post_save я сохраняю некоторые модели, в которых пользователь использует внешний ключ.
Контекст и ошибка очень похожи на этот вопрос django TransactionManagementError при использовании сигналов
Однако в этом случае ошибка возникает только при юнит-тестировании .
Это хорошо работает при ручном тестировании, но модульные тесты не пройдены.
Есть ли что-то, что я пропускаю?
Вот фрагменты кода:
views.py
@csrf_exempt
def mobileRegister(request):
if request.method == 'GET':
response = {"error": "GET request not accepted!!"}
return HttpResponse(json.dumps(response), content_type="application/json",status=500)
elif request.method == 'POST':
postdata = json.loads(request.body)
try:
# Get POST data which is to be used to save the user
username = postdata.get('phone')
password = postdata.get('password')
email = postdata.get('email',"")
first_name = postdata.get('first_name',"")
last_name = postdata.get('last_name',"")
user = User(username=username, email=email,
first_name=first_name, last_name=last_name)
user._company = postdata.get('company',None)
user._country_code = postdata.get('country_code',"+91")
user.is_verified=True
user._gcm_reg_id = postdata.get('reg_id',None)
user._gcm_device_id = postdata.get('device_id',None)
# Set Password for the user
user.set_password(password)
# Save the user
user.save()
signal.py
def create_user_profile(sender, instance, created, **kwargs):
if created:
company = None
companycontact = None
try: # Try to make userprofile with company and country code provided
user = User.objects.get(id=instance.id)
rand_pass = random.randint(1000, 9999)
company = Company.objects.get_or_create(name=instance._company,user=user)
companycontact = CompanyContact.objects.get_or_create(contact_type="Owner",company=company,contact_number=instance.username)
profile = UserProfile.objects.get_or_create(user=instance,phone=instance.username,verification_code=rand_pass,company=company,country_code=instance._country_code)
gcmDevice = GCMDevice.objects.create(registration_id=instance._gcm_reg_id,device_id=instance._gcm_reg_id,user=instance)
except Exception, e:
pass
tests.py
class AuthTestCase(TestCase):
fixtures = ['nextgencatalogs/fixtures.json']
def setUp(self):
self.user_data={
"phone":"0000000000",
"password":"123",
"first_name":"Gaurav",
"last_name":"Toshniwal"
}
def test_registration_api_get(self):
response = self.client.get("/mobileRegister/")
self.assertEqual(response.status_code,500)
def test_registration_api_post(self):
response = self.client.post(path="/mobileRegister/",
data=json.dumps(self.user_data),
content_type="application/json")
self.assertEqual(response.status_code,201)
self.user_data['username']=self.user_data['phone']
user = User.objects.get(username=self.user_data['username'])
# Check if the company was created
company = Company.objects.get(user__username=self.user_data['phone'])
self.assertIsInstance(company,Company)
# Check if the owner's contact is the same as the user's phone number
company_contact = CompanyContact.objects.get(company=company,contact_type="owner")
self.assertEqual(user.username,company_contact[0].contact_number)
Выслеживать:
======================================================================
ERROR: test_registration_api_post (nextgencatalogs.apps.catalogsapp.tests.AuthTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/Users/gauravtoshniwal1989/Developer/Web/Server/ngc/nextgencatalogs/apps/catalogsapp/tests.py", line 29, in test_registration_api_post
user = User.objects.get(username=self.user_data['username'])
File "/Users/gauravtoshniwal1989/Developer/Web/Server/ngc/ngcvenv/lib/python2.7/site-packages/django/db/models/manager.py", line 151, in get
return self.get_queryset().get(*args, **kwargs)
File "/Users/gauravtoshniwal1989/Developer/Web/Server/ngc/ngcvenv/lib/python2.7/site-packages/django/db/models/query.py", line 301, in get
num = len(clone)
File "/Users/gauravtoshniwal1989/Developer/Web/Server/ngc/ngcvenv/lib/python2.7/site-packages/django/db/models/query.py", line 77, in __len__
self._fetch_all()
File "/Users/gauravtoshniwal1989/Developer/Web/Server/ngc/ngcvenv/lib/python2.7/site-packages/django/db/models/query.py", line 854, in _fetch_all
self._result_cache = list(self.iterator())
File "/Users/gauravtoshniwal1989/Developer/Web/Server/ngc/ngcvenv/lib/python2.7/site-packages/django/db/models/query.py", line 220, in iterator
for row in compiler.results_iter():
File "/Users/gauravtoshniwal1989/Developer/Web/Server/ngc/ngcvenv/lib/python2.7/site-packages/django/db/models/sql/compiler.py", line 710, in results_iter
for rows in self.execute_sql(MULTI):
File "/Users/gauravtoshniwal1989/Developer/Web/Server/ngc/ngcvenv/lib/python2.7/site-packages/django/db/models/sql/compiler.py", line 781, in execute_sql
cursor.execute(sql, params)
File "/Users/gauravtoshniwal1989/Developer/Web/Server/ngc/ngcvenv/lib/python2.7/site-packages/django/db/backends/util.py", line 47, in execute
self.db.validate_no_broken_transaction()
File "/Users/gauravtoshniwal1989/Developer/Web/Server/ngc/ngcvenv/lib/python2.7/site-packages/django/db/backends/__init__.py", line 365, in validate_no_broken_transaction
"An error occurred in the current transaction. You can't "
TransactionManagementError: An error occurred in the current transaction. You can't execute queries until the end of the 'atomic' block.
----------------------------------------------------------------------
python
django
unit-testing
django-signals
Гаурав Тошнивал
источник
источник
Ответы:
Я столкнулся с этой же проблемой сам. Это вызвано причудой в том, как транзакции обрабатываются в более новых версиях Django в сочетании с unittest, который преднамеренно вызывает исключение.
У меня был unittest, который проверял, чтобы убедиться, что ограничение уникального столбца было применено, целенаправленно вызывая исключение IntegrityError:
В Django 1.4 это работает нормально. Однако в Django 1.5 / 1.6 каждый тест заключен в транзакцию, поэтому, если возникает исключение, он прерывает транзакцию, пока вы явно не откатите ее. Поэтому любые дальнейшие операции ORM в этой транзакции, такие как my
do_more_model_stuff()
, завершатся с этимdjango.db.transaction.TransactionManagementError
исключением.Как и caio, упомянутый в комментариях, решение состоит в том, чтобы зафиксировать ваше исключение с помощью
transaction.atomic
:Это предотвратит намеренно выброшенное исключение от прерывания транзакции всего юнит-теста.
источник
transaction.atomic()
блок, но я получил эту ошибку, и я понятия не имел, почему. Я воспользовался советом этого ответа и поместил вложенный атомный блок внутри моего атомного блока вокруг проблемной зоны. После этого он дал подробную ошибку ошибки целостности, которую я ударил, что позволило мне исправить свой код и сделать то, что я пытался сделать.TestCase
наследует,TransactionTestCase
так что не нужно это менять. Если вы не работаете с БД в тестовом режимеSimpleTestCase
.TestCase
наследуется от,TransactionTestCase
но его поведение совершенно иное: оно оборачивает каждый тестовый метод в транзакцию.TransactionTestCase
с другой стороны, возможно, его вводят в заблуждение: он обрезает таблицы для сброса БД - наименование, кажется, отражает то, что вы можете тестировать транзакции внутри теста, а не то, что тест обернут как транзакция!Так как @mkoistinen так и не сделал свой комментарий , ответ, я опубликую его предложение, чтобы людям не приходилось копаться в комментариях.
Из документов : TransactionTestCase может вызывать commit и rollback и наблюдать влияние этих вызовов на базу данных.
источник
SimpleTestCase
,allow_database_queries = True
должен быть добавлен в тестовый класс, чтобы он не выплевывалAssertionError("Database queries aren't allowed in SimpleTestCase...",)
.Если вы используете pytest-django, вы можете перейти
transaction=True
кdjango_db
декоратору, чтобы избежать этой ошибки.См. Https://pytest-django.readthedocs.io/en/latest/database.html#testing-transactions
источник
Вот еще один способ сделать это, основываясь на ответе на этот вопрос:
источник
Для меня предложенные исправления не сработали. В моих тестах я открываю несколько подпроцессов
Popen
для анализа / миграции миграции (например, один тест проверяет, нет ли изменений в модели).Для меня подклассы
SimpleTestCase
вместо вместо этогоTestCase
сделали свое дело.Обратите внимание, что
SimpleTestCase
не позволяет использовать базу данных.Хотя это не отвечает на первоначальный вопрос, я надеюсь, что это все равно поможет некоторым людям.
источник
Я получал эту ошибку при запуске модульных тестов в моей функции create_test_data с использованием django 1.9.7. Это работало в более ранних версиях Django.
Это выглядело так:
Мое решение было использовать вместо этого update_or_create:
источник
get_or_create()
работает также, кажется, что это .save (), что ему не нравится внутри украшенной транзакции.atomic () функции (моя не удалась только с одним вызовом там).У меня та же проблема, но у меня так
with transaction.atomic()
иTransactionTestCase
не получилось.python manage.py test -r
вместоpython manage.py test
меня это нормально, может быть, порядок исполнения имеет решающее значениезатем я нахожу документ о порядке, в котором выполняются тесты , в котором упоминается, какой тест будет выполняться первым.
Итак, я использую TestCase для взаимодействия с базой данных,
unittest.TestCase
для другого простого теста, он работает сейчас!источник
Ответ @kdazzle правильный. Я не пробовал, потому что люди говорили, что класс TestCase от Django является более часто используемым подклассом TransactionTestCase, поэтому я подумал, что это одно и то же использование. Но блог Джахонгира Рахмонова объяснил это лучше:
РЕДАКТИРОВАТЬ: Это не сработало, я думал, да, но нет.
Через 4 года они могли это исправить .......................................
источник
источник
Я была такая же проблема.
В моем случае я делал это
так что преобразовать его в
Убрал эту ошибку.
источник