TransactionManagementError 신호를 사용하는 동안 '원자'블록이 끝날 때까지 쿼리를 실행할 수 없습니다.
Django User 모델 인스턴스를 저장하려고 할 때 TransactionManagementError가 발생하고 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.
----------------------------------------------------------------------
I ran into this same problem myself. This is caused by a quirk in how transactions are handled in the newer versions of Django coupled with a unittest that intentionally triggers an exception.
I had a unittest that checked to make sure a unique column constraint was enforced by purposefully triggering an IntegrityError exception:
def test_constraint(self):
try:
# Duplicates should be prevented.
models.Question.objects.create(domain=self.domain, slug='barks')
self.fail('Duplicate question allowed.')
except IntegrityError:
pass
do_more_model_stuff()
In Django 1.4, this works fine. However, in Django 1.5/1.6, each test is wrapped in a transaction, so if an exception occurs, it breaks the transaction until you explicitly roll it back. Therefore, any further ORM operations in that transaction, such as my do_more_model_stuff()
, will fail with that django.db.transaction.TransactionManagementError
exception.
Like caio mentioned in the comments, the solution is to capture your exception with transaction.atomic
like:
from django.db import transaction
def test_constraint(self):
try:
# Duplicates should be prevented.
with transaction.atomic():
models.Question.objects.create(domain=self.domain, slug='barks')
self.fail('Duplicate question allowed.')
except IntegrityError:
pass
That will prevent the purposefully-thrown exception from breaking the entire unittest's transaction.
Since @mkoistinen never made his comment, an answer, I'll post his suggestion so people won't have to dig through comments.
consider just declaring your test class as a TransactionTestCase rather than just TestCase.
From the docs: A TransactionTestCase may call commit and rollback and observe the effects of these calls on the database.
If using pytest-django you can pass transaction=True
to the django_db
decorator to avoid this error.
See https://pytest-django.readthedocs.io/en/latest/database.html#testing-transactions
Django itself has the TransactionTestCase which allows you to test transactions and will flush the database between tests to isolate them. The downside of this is that these tests are much slower to set up due to the required flushing of the database. pytest-django also supports this style of tests, which you can select using an argument to the django_db mark:
@pytest.mark.django_db(transaction=True)
def test_spam():
pass # test relying on transactions
For me, the proposed fixes did not work. In my tests, I open some subprocesses with Popen
to analyze/lint migrations (e.g. one test checks if there are no model changes).
For me, subclassing from SimpleTestCase
instead of TestCase
did do the trick.
Note that SimpleTestCase
doesn't allow to use the database.
While this does not answer the original question, I hope this helps some people anyway.
I was getting this error on running unit tests in my create_test_data function using django 1.9.7. It worked in earlier versions of django.
It looked like this:
cls.localauth,_ = Organisation.objects.get_or_create(organisation_type=cls.orgtypeLA, name='LA for test', email_general='test@test.com', address='test', postcode='test', telephone='test')
cls.chamber,_ = Organisation.objects.get_or_create(organisation_type=cls.orgtypeC, name='chamber for test', email_general='test@test.com', address='test', postcode='test', telephone='test')
cls.lawfirm,_ = Organisation.objects.get_or_create(organisation_type=cls.orgtypeL, name='lawfirm for test', email_general='test@test.com', address='test', postcode='test', telephone='test')
cls.chamber.active = True
cls.chamber.save()
cls.localauth.active = True
cls.localauth.save() <---- error here
cls.lawfirm.active = True
cls.lawfirm.save()
My solution was to use update_or_create instead:
cls.localauth,_ = Organisation.objects.update_or_create(organisation_type=cls.orgtypeLA, name='LA for test', email_general='test@test.com', address='test', postcode='test', telephone='test', defaults={'active': True})
cls.chamber,_ = Organisation.objects.update_or_create(organisation_type=cls.orgtypeC, name='chamber for test', email_general='test@test.com', address='test', postcode='test', telephone='test', defaults={'active': True})
cls.lawfirm,_ = Organisation.objects.update_or_create(organisation_type=cls.orgtypeL, name='lawfirm for test', email_general='test@test.com', address='test', postcode='test', telephone='test', defaults={'active': True})
I have the same issue, but with transaction.atomic()
and TransactionTestCase
didn't work for me.
python manage.py test -r
instead of python manage.py test
is ok for me, maybe the order of execution is crucial
then i find a doc about Order in which tests are executed, It mentions which test will run first.
So, i use TestCase for database interaction, unittest.TestCase
for other simple test, it works now!
The answer of @kdazzle is correct. I didnt try it because people said that 'Django’s TestCase class is a more commonly used subclass of TransactionTestCase' so I thought it was the same use one or another. But the blog of Jahongir Rahmonov explained it better:
the TestCase class wraps the tests within two nested atomic() blocks: one for the whole class and one for each test. This is where TransactionTestCase should be used. It does not wrap the tests with atomic() block and thus you can test your special methods that require a transaction without any problem.
EDIT: It didn't work, I thought yes, but NO.
In 4 years they could fixed this.......................................
I had the same issue.
In My Case I was doing this
author.tasks.add(tasks)
so converting it to
author.tasks.add(*tasks)
Removed that error.
'IT박스' 카테고리의 다른 글
스프링 부트 : EmbeddedServletContainerFactory 빈이 없어서 EmbeddedWebApplicationContext를 시작할 수 없음 (0) | 2020.05.30 |
---|---|
Android 용으로 개발할 때 Eclipse 콘솔에 메시지를 출력하는 방법 (0) | 2020.05.30 |
Rails의 특정 액션에 대한 인증 토큰을 어떻게 무시합니까? (0) | 2020.05.30 |
IntelliJ IDEA 및 기타 Jetbrains IDE에서 "절전 모드"란 무엇입니까? (0) | 2020.05.30 |
Google Web Fonts 링크 또는 가져 오기를 포함합니까? (0) | 2020.05.30 |