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.
----------------------------------------------------------------------
답변
나는이 같은 문제를 직접 만났다. 이것은 최신 버전의 Django에서 트랜잭션이 처리되는 방식에 문제가 있기 때문에 의도적으로 예외를 트리거하는 단위 테스트와 결합됩니다.
IntegrityError 예외를 의도적으로 트리거하여 고유 한 열 제약 조건이 적용되었는지 확인한 단위 테스트를 수행했습니다.
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()
Django 1.4에서는 이것이 잘 작동합니다. 그러나 Django 1.5 / 1.6에서는 각 테스트가 트랜잭션으로 래핑되므로 예외가 발생하면 명시 적으로 롤백 할 때까지 트랜잭션이 중단됩니다. 따라서 my와 같은 해당 트랜잭션의 추가 ORM 조작은 예외로 do_more_model_stuff()
실패합니다 django.db.transaction.TransactionManagementError
.
의견에 언급 된 caio와 같이 해결책은 다음과 transaction.atomic
같이 예외를 캡처하는 것입니다.
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
이렇게하면 의도적으로 throw 된 예외가 전체 단위 테스트의 트랜잭션을 위반하지 못하게됩니다.
답변
@mkoistinen은 자신의 의견 , 답변을 한 적이 없으므로 사람들이 의견을 파지 않아도되도록 제안을 게시 할 것입니다.
테스트 클래스를 TestCase가 아닌 TransactionTestCase로 선언하는 것을 고려하십시오.
로부터 문서 , • TransactionTestCase 커밋 전화를 롤백하고 데이터베이스에 이러한 호출의 효과를 관찰 할 수 있습니다.
답변
사용하는 경우 당신은 통과 할 수 pytest을-장고 transaction=True
받는 사람 django_db
이 오류를 방지하기 위해 장식.
https://pytest-django.readthedocs.io/en/latest/database.html#testing-transactions를 참조 하십시오.
장고 자체에는 TransactionTestCase가있어 트랜잭션을 테스트하고 테스트 사이에 데이터베이스를 플러시하여 격리합니다. 이것의 단점은 데이터베이스를 플러시해야하기 때문에 이러한 테스트를 설정하는 것이 훨씬 느리다는 것입니다. pytest-django는 다음과 같은 스타일의 테스트도 지원합니다. django_db 표시에 대한 인수를 사용하여 선택할 수 있습니다.
@pytest.mark.django_db(transaction=True)
def test_spam():
pass # test relying on transactions
답변
나에게 제안 된 수정 사항이 작동하지 않았습니다. 테스트에서 Popen
마이그레이션을 분석 / 린트하기 위해 일부 하위 프로세스를 엽니 다 (예 : 모델 변경이없는 경우 하나의 테스트 확인).
나를 위해 SimpleTestCase
대신 서브 클래스를 사용 TestCase
하여 트릭을 수행했습니다.
참고 SimpleTestCase
데이터베이스를 사용하는 것을 허용하지 않습니다.
이것이 원래의 질문에 대한 답은 아니지만 어쨌든 일부 사람들에게 도움이되기를 바랍니다.
답변
이 질문에 대한 답변을 바탕으로 다른 방법을 사용하십시오.
with transaction.atomic():
self.assertRaises(IntegrityError, models.Question.objects.create, **{'domain':self.domain, 'slug':'barks'})
답변
django 1.9.7을 사용하여 create_test_data 함수에서 단위 테스트를 실행할 때이 오류가 발생했습니다. 이전 버전의 장고에서 작동했습니다.
다음과 같이 보였습니다 :
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()
내 솔루션은 대신 update_or_create를 사용하는 것입니다.
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})
답변
저도 같은 문제를 가지고 있지만, with transaction.atomic()
그리고 TransactionTestCase
나를 위해 작동하지 않았다.
python manage.py test -r
대신에 python manage.py test
나에게는 괜찮습니다. 어쩌면 실행 순서가 중요합니다.
그런 다음 테스트가 실행되는 순서에 대한 문서를 찾습니다. 어떤 테스트가 먼저 실행 될지 언급합니다.
따라서 데이터베이스 상호 작용에 TestCase를 사용 unittest.TestCase
하고 다른 간단한 테스트에는 지금 작동합니다!