Recibo TransactionManagementError cuando intento guardar una instancia de modelo de usuario de Django y, en su señal post_save, guardo algunos modelos que tienen al usuario como clave foránea.
El contexto y el error es bastante similar a esta pregunta django TransactionManagementError cuando se usan señales
Sin embargo, en este caso, el error ocurre solo durante la prueba de la unidad .
Funciona bien en las pruebas manuales, pero las pruebas unitarias fallan.
¿Hay algo que me falta?
Aquí están los fragmentos 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)
Rastrear:
======================================================================
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
fuente
fuente
Respuestas:
Me encontré con este mismo problema yo mismo. Esto se debe a una peculiaridad en la forma en que se manejan las transacciones en las versiones más recientes de Django junto con una prueba unitaria que desencadena intencionalmente una excepción.
Tuve una prueba de unidad que verificó para asegurarme de que se aplicara una restricción de columna única al activar intencionalmente una excepción IntegrityError:
En Django 1.4, esto funciona bien. Sin embargo, en Django 1.5 / 1.6, cada prueba se envuelve en una transacción, por lo que si se produce una excepción, se rompe la transacción hasta que la deshaga explícitamente. Por lo tanto, cualquier otra operación ORM en esa transacción, como my
do_more_model_stuff()
, fallará con esadjango.db.transaction.TransactionManagementError
excepción.Como caio mencionado en los comentarios, la solución es capturar su excepción con me
transaction.atomic
gusta:Eso evitará que la excepción lanzada a propósito rompa toda la transacción de unittest.
fuente
transaction.atomic()
bloqueo, pero recibí este error y no tenía idea de por qué. Tomé el consejo de esta respuesta y puse un bloque atómico anidado dentro de mi bloque atómico alrededor del área problemática. Después de eso, dio un error detallado del error de integridad que golpeé, lo que me permitió corregir mi código y hacer lo que estaba tratando de hacer.TestCase
está heredando,TransactionTestCase
así que no hay necesidad de cambiar eso. Si no opera en DB en uso de pruebaSimpleTestCase
.TestCase
hereda de,TransactionTestCase
pero su comportamiento es bastante diferente: envuelve cada método de prueba en una transacción.TransactionTestCase
, por otro lado, quizás se denomina de manera engañosa: trunca las tablas para restablecer la base de datos; la denominación parece reflejar que puede probar transacciones dentro de una prueba, ¡no que la prueba esté envuelta como una transacción!Dado que @mkoistinen nunca hizo su comentario , una respuesta, publicaré su sugerencia para que la gente no tenga que buscar los comentarios.
De los documentos : Un TransactionTestCase puede llamar a commit y rollback y observar los efectos de estas llamadas en la base de datos.
fuente
SimpleTestCase
,allow_database_queries = True
debe agregarse dentro de la clase de prueba, para que no escupe unAssertionError("Database queries aren't allowed in SimpleTestCase...",)
.Si usa pytest-django, puede pasar
transaction=True
aldjango_db
decorador para evitar este error.Ver https://pytest-django.readthedocs.io/en/latest/database.html#testing-transactions
fuente
Para mí, las correcciones propuestas no funcionaron. En mis pruebas, abro algunos subprocesos con
Popen
para analizar / migrar pelusas (por ejemplo, una prueba verifica si no hay cambios en el modelo).Para mí, la subclase de en
SimpleTestCase
lugar deTestCase
hacer el truco.Tenga en cuenta que
SimpleTestCase
no permite usar la base de datos.Si bien esto no responde a la pregunta original, espero que esto ayude a algunas personas de todos modos.
fuente
Aquí hay otra forma de hacerlo, basada en la respuesta a esta pregunta:
fuente
Recibí este error al ejecutar pruebas unitarias en mi función create_test_data usando django 1.9.7. Funcionó en versiones anteriores de django.
Se veía así:
Mi solución fue usar update_or_create en su lugar:
fuente
get_or_create()
funciona bien, parece que es el .save () que no le gusta dentro de una función decorada de la transacción.atomic () (la mía falló con solo 1 llamada allí).Tengo el mismo problema, pero
with transaction.atomic()
yTransactionTestCase
no funcionó para mí.python manage.py test -r
en lugar depython manage.py test
está bien para mí, tal vez el orden de ejecución es crucialluego encuentro un documento sobre Order en el que se ejecutan las pruebas . Menciona qué prueba se ejecutará primero.
Entonces, uso TestCase para la interacción de la base de datos,
unittest.TestCase
para otra prueba simple, ¡funciona ahora!fuente
La respuesta de @kdazzle es correcta. No lo intenté porque la gente decía que 'la clase TestCase de Django es una subclase más utilizada de TransactionTestCase', así que pensé que era el mismo uso uno u otro. Pero el blog de Jahongir Rahmonov lo explicó mejor:
EDITAR: No funcionó, pensé que sí, pero NO.
En 4 años podrían arreglar esto .......................................
fuente
fuente
Tuve el mismo problema.
En mi caso estaba haciendo esto
convirtiéndolo a
Eliminado ese error.
fuente