Estou recebendo TransactionManagementError ao tentar salvar uma instância de modelo de usuário do Django e em seu sinal post_save, estou salvando alguns modelos que têm o usuário como chave estrangeira.
O contexto e o erro são bastante semelhantes a esta pergunta django TransactionManagementError ao usar sinais
No entanto, nesse caso, o erro ocorre apenas durante o teste de unidade .
Funciona bem em testes manuais, mas os testes de unidade falham.
Falta alguma coisa?
Aqui estão os trechos de código:
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)
Traceback:
======================================================================
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
Gaurav Toshniwal
fonte
fonte
Respostas:
Eu me deparei com esse mesmo problema. Isso é causado por uma peculiaridade de como as transações são tratadas nas versões mais recentes do Django, juntamente com um mais unittest que intencionalmente aciona uma exceção.
Eu tive um teste mais unificado que verifiquei para garantir que uma restrição de coluna exclusiva fosse imposta ao acionar propositadamente uma exceção IntegrityError:
No Django 1.4, isso funciona bem. No entanto, no Django 1.5 / 1.6, cada teste é agrupado em uma transação; portanto, se ocorrer uma exceção, ele interrompe a transação até que você a revele explicitamente. Portanto, qualquer operação ORM adicional nessa transação, como a minha
do_more_model_stuff()
, falhará com essadjango.db.transaction.TransactionManagementError
exceção.Como o caio mencionado nos comentários, a solução é capturar sua exceção com o seguinte
transaction.atomic
:Isso impedirá que a exceção lançada propositalmente interrompa toda a transação do unittest.
fonte
transaction.atomic()
bloqueio, mas recebi esse erro e não fazia ideia do porquê. Eu segui o conselho desta resposta e coloquei um bloco atômico aninhado dentro do meu bloco atômico em torno da área problemática. Depois disso, deu um erro detalhado do erro de integridade que acertei, permitindo que eu corrigisse meu código e fizesse o que estava tentando fazer.TestCase
está herdando,TransactionTestCase
então não há necessidade de mudar isso. Se você não operar no DB em uso de testeSimpleTestCase
.TestCase
herda,TransactionTestCase
mas seu comportamento é bem diferente: envolve cada método de teste em uma transação.TransactionTestCase
, por outro lado, talvez tenha um nome enganador: trunca tabelas para redefinir o banco de dados - a nomeação parece refletir que você pode testar transações dentro de um teste, não que o teste seja encapsulado como uma transação!Como @mkoistinen nunca fez seu comentário , responda, vou postar sua sugestão para que as pessoas não precisem procurar nos comentários.
Dos documentos : Um TransactionTestCase pode chamar confirmação e reversão e observar os efeitos dessas chamadas no banco de dados.
fonte
SimpleTestCase
,allow_database_queries = True
deve ser adicionado dentro da classe de teste, para que não cuspa umAssertionError("Database queries aren't allowed in SimpleTestCase...",)
.Se estiver usando pytest-django, você pode passar
transaction=True
para odjango_db
decorador para evitar esse erro.Consulte https://pytest-django.readthedocs.io/en/latest/database.html#testing-transactions
fonte
Para mim, as correções propostas não funcionaram. Nos meus testes, abro alguns subprocessos
Popen
para analisar / lint migrações (por exemplo, um teste verifica se não há alterações no modelo).Para mim, subclassificar de em
SimpleTestCase
vez deTestCase
fez o truque.Observe que
SimpleTestCase
não permite usar o banco de dados.Embora isso não responda à pergunta original, espero que isso ajude algumas pessoas de qualquer maneira.
fonte
Aqui está outra maneira de fazê-lo, com base na resposta a esta pergunta:
fonte
Eu estava recebendo esse erro ao executar testes de unidade na minha função create_test_data usando o django 1.9.7. Funcionou em versões anteriores do django.
Parecia assim:
Minha solução foi usar update_or_create:
fonte
get_or_create()
funciona bem, parece que é a .save () que não gosta dentro de uma função decorada transaction.atomic () (a minha falhou com apenas uma chamada).Eu tenho o mesmo problema, mas
with transaction.atomic()
eTransactionTestCase
não funcionou para mim.python manage.py test -r
em vez depython manage.py test
está bem para mim, talvez a ordem de execução seja crucialentão, encontro um documento sobre a ordem na qual os testes são executados . Ele menciona qual teste será executado primeiro.
Então, eu uso o TestCase para interação com o banco de dados,
unittest.TestCase
para outro teste simples, ele funciona agora!fonte
A resposta de @kdazzle está correta. Eu não tentei porque as pessoas diziam que 'a classe TestCase do Django é uma subclasse mais usada do TransactionTestCase', então pensei que era o mesmo uso de um ou outro. Mas o blog de Jahongir Rahmonov explicou melhor:
EDIT: Não deu certo, pensei que sim, mas NÃO.
Em 4 anos eles poderiam consertar isso .......................................
fonte
fonte
Eu tive o mesmo problema.
No meu caso, eu estava fazendo isso
então convertendo para
Removido esse erro.
fonte