Dostaję TransactionManagementError podczas próby zapisania instancji modelu użytkownika Django i w jego sygnale post_save zapisuję niektóre modele, w których użytkownik jest kluczem obcym.
Kontekst i błąd są bardzo podobne do tego pytania django TransactionManagementError podczas używania sygnałów
Jednak w tym przypadku błąd występuje tylko podczas testowania jednostkowego .
Działa dobrze w testach ręcznych, ale testy jednostkowe kończą się niepowodzeniem.
Czy czegoś mi brakuje?
Oto fragmenty kodu:
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
źródło
źródło
Odpowiedzi:
Sam napotkałem ten sam problem. Jest to spowodowane dziwactwem w obsłudze transakcji w nowszych wersjach Django w połączeniu z jednostką, która celowo wyzwala wyjątek.
Miałem unittest, który sprawdził, aby upewnić się, że wymuszono unikalne ograniczenie kolumny, celowo uruchamiając wyjątek IntegrityError:
W Django 1.4 to działa dobrze. Jednak w Django 1.5 / 1.6 każdy test jest zawinięty w transakcję, więc jeśli wystąpi wyjątek, przerywa transakcję do momentu jej jawnego wycofania. Dlatego wszelkie dalsze operacje ORM w tej transakcji, takie jak moja
do_more_model_stuff()
, zakończą się niepowodzeniem z tymdjango.db.transaction.TransactionManagementError
wyjątkiem.Podobnie jak caio wspomniany w komentarzach, rozwiązaniem jest uchwycenie wyjątku za pomocą
transaction.atomic
:Zapobiegnie to celowo zgłoszonemu wyjątkowi, który złamie całą transakcję najinteligentniejszego.
źródło
transaction.atomic()
blok, ale mam ten błąd i nie miałem pojęcia dlaczego. Skorzystałem z tej odpowiedzi i umieściłem zagnieżdżony blok atomowy w moim bloku atomowym wokół obszaru problemów. Następnie podał szczegółowy błąd trafienia błędu integralności, co pozwoliło mi naprawić kod i zrobić to, co próbowałem zrobić.TestCase
dziedziczy po,TransactionTestCase
więc nie trzeba tego zmieniać. Jeśli nie korzystasz z DB podczas używania testowegoSimpleTestCase
.TestCase
dziedziczy po,TransactionTestCase
ale jego zachowanie jest zupełnie inne: zawija każdą metodę testową w transakcji.TransactionTestCase
, z drugiej strony, być może ma mylącą nazwę: obcina tabele, aby zresetować db - nazwa wydaje się odzwierciedlać, że można testować transakcje w ramach testu, a nie, że test jest zawinięty jako transakcja!Ponieważ @mkoistinen nigdy nie skomentował odpowiedzi, opublikuję jego sugestię, aby ludzie nie musieli przekopywać komentarzy.
Z dokumentacji : TransactionTestCase może wywoływać zatwierdzanie i wycofywanie oraz obserwować wpływ tych wywołań na bazę danych.
źródło
SimpleTestCase
,allow_database_queries = True
należy dodać wewnątrz klasy testowej, więc nie plućAssertionError("Database queries aren't allowed in SimpleTestCase...",)
.Jeśli używasz pytest-django, możesz przejść
transaction=True
dodjango_db
dekoratora, aby uniknąć tego błędu.Zobacz https://pytest-django.readthedocs.io/en/latest/database.html#testing-transactions
źródło
Dla mnie proponowane poprawki nie działały. W moich testach otwieram niektóre podprocesy za pomocą
Popen
aby analizować / migrować kłaczki (np. Jeden test sprawdza, czy nie ma zmian w modelu).Dla mnie podklasa z
SimpleTestCase
zamiastTestCase
wykonałem.Zauważ, że
SimpleTestCase
nie zezwala na korzystanie z bazy danych.Chociaż to nie odpowiada na pierwotne pytanie, mam nadzieję, że to i tak pomaga niektórym ludziom.
źródło
Oto inny sposób, aby to zrobić, w oparciu o odpowiedź na to pytanie:
źródło
Ten błąd występował podczas uruchamiania testów jednostkowych w mojej funkcji create_test_data przy użyciu django 1.9.7. Działa we wcześniejszych wersjach django.
Wyglądało to tak:
Moim rozwiązaniem było użycie zamiast tego update_or_create:
źródło
get_or_create()
działa również, wygląda na to, że to .save () nie lubi wewnątrz dekorowanej funkcji transakcji.atomic () (moja nie powiodła się z tylko 1 wywołaniem).Mam ten sam problem, ale
with transaction.atomic()
iTransactionTestCase
nie działa dla mnie.python manage.py test -r
zamiastpython manage.py test
jest dla mnie ok, być może kolejność wykonania jest kluczowanastępnie znajduję dokument dotyczący kolejności wykonywania testów Wspomina, który test zostanie uruchomiony jako pierwszy.
Tak więc używam TestCase do interakcji z bazą danych,
unittest.TestCase
do innego prostego testu, to działa teraz!źródło
Odpowiedź @kdazzle jest poprawna. Nie próbowałem tego, ponieważ ludzie mówili, że „Klasa TestCase Django jest częściej używaną podklasą TransactionTestCase”, więc pomyślałem, że to samo lub inne zastosowanie. Ale blog Jahongira Rahmonova wyjaśnił to lepiej:
EDYCJA: Nie działało, myślałem tak, ale NIE.
Za 4 lata mogliby to naprawić .......................................
źródło
źródło
Miałem ten sam problem.
W moim przypadku to robiłem
więc konwertując to na
Usunięto ten błąd.
źródło