J'obtiens TransactionManagementError en essayant de sauvegarder une instance de modèle Django User et dans son signal post_save, j'enregistre certains modèles qui ont l'utilisateur comme clé étrangère.
Le contexte et l'erreur sont assez similaires à cette question django TransactionManagementError lors de l'utilisation de signaux
Cependant, dans ce cas, l'erreur se produit uniquement lors des tests unitaires .
Cela fonctionne bien dans les tests manuels, mais les tests unitaires échouent.
Y a-t-il quelque chose qui me manque?
Voici les extraits de code:
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
la source
la source
Réponses:
J'ai moi-même rencontré le même problème. Cela est dû à une bizarrerie dans la façon dont les transactions sont gérées dans les nouvelles versions de Django, couplées à un test unitaire qui déclenche intentionnellement une exception.
J'ai eu un test unitaire qui a vérifié pour s'assurer qu'une contrainte de colonne unique était appliquée en déclenchant délibérément une exception IntegrityError:
Dans Django 1.4, cela fonctionne très bien. Cependant, dans Django 1.5 / 1.6, chaque test est encapsulé dans une transaction, donc si une exception se produit, il interrompt la transaction jusqu'à ce que vous l'annuliez explicitement. Par conséquent, toute autre opération ORM dans cette transaction, telle que my
do_more_model_stuff()
, échouera avec cettedjango.db.transaction.TransactionManagementError
exception.Comme caio mentionné dans les commentaires, la solution est de capturer votre exception avec
transaction.atomic
comme:Cela empêchera l'exception lancée délibérément de rompre toute la transaction d'unittest.
la source
transaction.atomic()
blocage, mais j'ai eu cette erreur et je ne savais pas pourquoi. J'ai suivi les conseils de cette réponse et mis un bloc atomique imbriqué à l'intérieur de mon bloc atomique autour de la zone de problème. Après cela, il a donné une erreur détaillée de l'erreur d'intégrité que j'ai frappée, me permettant de corriger mon code et de faire ce que j'essayais de faire.TestCase
hériteTransactionTestCase
donc pas besoin de changer cela. Si vous n'opérez pas sur DB en cours d'utilisationSimpleTestCase
.TestCase
hérite deTransactionTestCase
mais son comportement est assez différent: il encapsule chaque méthode de test dans une transaction.TransactionTestCase
, d'un autre côté, son nom est peut-être trompeur: il tronque les tables pour réinitialiser la base de données - le nom semble refléter que vous pouvez tester les transactions dans un test, pas que le test est enveloppé comme une transaction!Puisque @mkoistinen n'a jamais fait son commentaire , une réponse, je posterai sa suggestion pour que les gens n'aient pas à fouiller dans les commentaires.
À partir de la documentation : Un TransactionTestCase peut appeler commit et rollback et observer les effets de ces appels sur la base de données.
la source
SimpleTestCase
,allow_database_queries = True
doit être ajouté à l'intérieur de la classe de test, afin qu'il ne crache pas de fichierAssertionError("Database queries aren't allowed in SimpleTestCase...",)
.Si vous utilisez pytest-django, vous pouvez passer
transaction=True
audjango_db
décorateur pour éviter cette erreur.Voir https://pytest-django.readthedocs.io/en/latest/database.html#testing-transactions
la source
Voici une autre façon de le faire, basée sur la réponse à cette question:
la source
Pour moi, les correctifs proposés n'ont pas fonctionné. Dans mes tests, j'ouvre des sous-processus avec
Popen
pour analyser / lint les migrations (par exemple un test vérifie s'il n'y a pas de changement de modèle).Pour moi, sous-classer de
SimpleTestCase
au lieu deTestCase
faire l'affaire.Notez que
SimpleTestCase
cela ne permet pas d'utiliser la base de données.Bien que cela ne réponde pas à la question initiale, j'espère que cela aidera certaines personnes de toute façon.
la source
J'obtenais cette erreur lors de l'exécution de tests unitaires dans ma fonction create_test_data en utilisant django 1.9.7. Cela fonctionnait dans les versions antérieures de django.
Cela ressemblait à ceci:
Ma solution était d'utiliser à la place update_or_create:
la source
get_or_create()
fonctionne aussi bien, il semble que ce soit le .save () qu'il n'aime pas dans une fonction décorée transaction.atomic () (la mienne a échoué avec un seul appel).J'ai le même problème, mais
with transaction.atomic()
etTransactionTestCase
n'a pas fonctionné pour moi.python manage.py test -r
au lieu depython manage.py test
ça me va, peut-être que l'ordre d'exécution est crucialpuis je trouve un document sur l' ordre dans lequel les tests sont exécutés , il mentionne quel test sera exécuté en premier.
Donc, j'utilise TestCase pour l'interaction avec la base de données,
unittest.TestCase
pour d'autres tests simples, cela fonctionne maintenant!la source
La réponse de @kdazzle est correcte. Je ne l'ai pas essayé parce que les gens ont dit que «la classe TestCase de Django est une sous-classe plus couramment utilisée de TransactionTestCase», alors j'ai pensé que c'était la même utilisation l'un ou l'autre. Mais le blog de Jahongir Rahmonov l' expliquait mieux:
EDIT: Cela n'a pas fonctionné, j'ai pensé que oui, mais NON.
En 4 ans, ils pourraient régler ce problème .......................................
la source
la source
J'ai eu le même problème.
Dans mon cas, je faisais ça
alors le convertir en
Supprimé cette erreur.
la source