Ich erhalte TransactionManagementError, wenn ich versuche, eine Django-Benutzermodellinstanz zu speichern, und in ihrem post_save-Signal speichere ich einige Modelle, die den Benutzer als Fremdschlüssel haben.
Der Kontext und Fehler ist dieser Frage ziemlich ähnlich. Django TransactionManagementError bei der Verwendung von Signalen
In diesem Fall tritt der Fehler jedoch nur beim Testen der Einheit auf .
Es funktioniert gut beim manuellen Testen, aber Unit-Tests schlagen fehl.
Fehlt mir etwas?
Hier sind die Codefragmente:
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)
Zurück verfolgen:
======================================================================
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
quelle
quelle
Antworten:
Ich bin selbst auf dasselbe Problem gestoßen. Dies wird durch eine Eigenart in der Art und Weise verursacht, wie Transaktionen in den neueren Versionen von Django behandelt werden, verbunden mit einer Unittest, die absichtlich eine Ausnahme auslöst.
Ich hatte eine Unittest, die überprüfte, ob eine eindeutige Spalteneinschränkung durch gezieltes Auslösen einer IntegrityError-Ausnahme erzwungen wurde:
In Django 1.4 funktioniert dies einwandfrei. In Django 1.5 / 1.6 wird jedoch jeder Test in eine Transaktion eingeschlossen. Wenn also eine Ausnahme auftritt, wird die Transaktion unterbrochen, bis Sie sie explizit zurücksetzen. Daher schlagen alle weiteren ORM-Vorgänge in dieser Transaktion, wie z. B. my
do_more_model_stuff()
, mit dieserdjango.db.transaction.TransactionManagementError
Ausnahme fehl .Wie in den Kommentaren erwähnt, besteht die Lösung darin, Ihre Ausnahme
transaction.atomic
wie folgt zu erfassen :Dadurch wird verhindert, dass die absichtlich ausgelöste Ausnahme die gesamte Transaktion der Unittest unterbricht.
quelle
transaction.atomic()
Block, aber ich bekam diesen Fehler und ich hatte keine Ahnung warum. Ich nahm den Rat dieser Antwort an und legte einen verschachtelten Atomblock in meinen Atomblock um den Problembereich. Danach gab es einen detaillierten Fehler des Integritätsfehlers, den ich traf, sodass ich meinen Code reparieren und das tun konnte, was ich versuchte.TestCase
erbt vonTransactionTestCase
so keine Notwendigkeit, das zu ändern. Wenn Sie im Test nicht mit DB arbeitenSimpleTestCase
.TestCase
erbt von,TransactionTestCase
aber sein Verhalten ist ganz anders: Es umschließt jede Testmethode in einer Transaktion.TransactionTestCase
Andererseits wird es möglicherweise irreführend benannt: Es schneidet Tabellen ab, um die Datenbank zurückzusetzen - die Benennung scheint zu reflektieren, dass Sie Transaktionen innerhalb eines Tests testen können, nicht, dass der Test als Transaktion verpackt ist!Da @mkoistinen nie seinen Kommentar abgegeben hat , eine Antwort, werde ich seinen Vorschlag posten, damit die Leute nicht durch Kommentare graben müssen.
Aus den Dokumenten : Ein TransactionTestCase kann Commit und Rollback aufrufen und die Auswirkungen dieser Aufrufe auf die Datenbank beobachten.
quelle
SimpleTestCase
,allow_database_queries = True
müssen innerhalb der Testklasse hinzugefügt werden, so dass es keine nicht spuckenAssertionError("Database queries aren't allowed in SimpleTestCase...",)
.Wenn Sie Pytest-Django verwenden, können Sie bestehen
transaction=True
an die übergebendjango_db
Dekorateur übergeben, um diesen Fehler zu vermeiden.Siehe https://pytest-django.readthedocs.io/en/latest/database.html#testing-transactions
quelle
Für mich haben die vorgeschlagenen Korrekturen nicht funktioniert. In meinen Tests öffne ich einige Unterprozesse mit
Popen
, um Migrationen zu analysieren / fusseln (z. B. prüft ein Test, ob es keine Modelländerungen gibt).Für mich hat die Unterklasse von
SimpleTestCase
stattTestCase
den Trick getan.Beachten Sie, dass
SimpleTestCase
die Datenbank nicht verwendet werden kann.Dies beantwortet zwar nicht die ursprüngliche Frage, aber ich hoffe, dass dies einigen Menschen trotzdem hilft.
quelle
Hier ist eine andere Möglichkeit, basierend auf der Antwort auf diese Frage:
quelle
Ich habe diesen Fehler beim Ausführen von Komponententests in meiner Funktion create_test_data mit django 1.9.7 erhalten. Es hat in früheren Versionen von Django funktioniert.
Es sah so aus:
Meine Lösung bestand darin, stattdessen update_or_create zu verwenden:
quelle
get_or_create()
funktioniert auch, es scheint, dass es die .save () ist, die es in einer mit transaction.atomic () dekorierten Funktion nicht mag (meine ist mit nur 1 Aufruf fehlgeschlagen).Ich habe das gleiche Problem, aber
with transaction.atomic()
undTransactionTestCase
nicht für mich arbeiten.python manage.py test -r
anstattpython manage.py test
ist für mich in Ordnung, vielleicht ist die Reihenfolge der Ausführung entscheidenddann finde ich ein Dokument über die Reihenfolge, in der Tests ausgeführt werden werden. Es erwähnt, welcher Test zuerst ausgeführt wird.
Also benutze ich TestCase für die Datenbankinteraktion,
unittest.TestCase
für andere einfache Tests funktioniert es jetzt!quelle
Die Antwort von @kdazzle ist richtig. Ich habe es nicht ausprobiert, weil die Leute sagten, dass 'Djangos TestCase-Klasse eine häufiger verwendete Unterklasse von TransactionTestCase ist', also dachte ich, dass es die gleiche Verwendung ist. Aber der Blog von Jahongir Rahmonov erklärte es besser:
EDIT: Es hat nicht funktioniert, ich dachte ja, aber nein.
In 4 Jahren konnten sie dies beheben .......................................
quelle
quelle
Ich hatte das gleiche Problem.
In meinem Fall habe ich das getan
also konvertiere es zu
Dieser Fehler wurde behoben.
quelle