TransactionManagementError "आप संकेतों का उपयोग करते समय 'परमाणु' ब्लॉक के अंत तक प्रश्नों का निष्पादन नहीं कर सकते हैं, लेकिन परीक्षण परीक्षण के दौरान


194

जब मैं एक Django उपयोगकर्ता मॉडल इंस्टेंस और इसके post_save सिग्नल को सहेजने का प्रयास कर रहा हूं, तो मैं TransactionManagementError प्राप्त कर रहा हूं, मैं कुछ मॉडल सहेज रहा हूं जिनके पास उपयोगकर्ता के पास विदेशी कुंजी है।

संकेत का उपयोग करते समय संदर्भ और त्रुटि इस प्रश्न के समान है django TransactionManagementError

हालाँकि, इस स्थिति में, त्रुटि केवल यूनिट परीक्षण करते समय होती है ।

यह मैन्युअल परीक्षण में अच्छा काम करता है, लेकिन यूनिट परीक्षण विफल रहता है।

क्या ऐसा कुछ है जो मुझे याद आ रहा है?

यहाँ कोड स्निपेट हैं:

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)

वापस ट्रेस करें:

======================================================================
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.

----------------------------------------------------------------------

डॉक्स से: "ए टेस्टकेस, दूसरी ओर, एक परीक्षण के बाद तालिकाओं को छोटा नहीं करता है। इसके बजाय, यह परीक्षण कोड के अंत में लुढ़का हुआ डेटाबेस लेनदेन में परीक्षण कोड को संलग्न करता है। दोनों स्पष्ट रूप से कॉम डॉट कॉम की तरह काम करता है। () और निहितार्थ जो लेनदेन के कारण हो सकते हैं। परमाणु () को एक एनओपी ऑपरेशन से बदल दिया जाता है। यह गारंटी देता है कि परीक्षण के अंत में रोलबैक डेटाबेस को इसकी प्रारंभिक स्थिति में पुनर्स्थापित करता है। "
गौरव तोषनीवाल

6
मुझे मेरी समस्या का पता चला। इस तरह का एक इंटेग्रिटीइटर अपवाद था "आज़माएं: ... सिवाय इंटीग्रिटी इटरर के: ..." मुझे जो करना था वह ट्राइ-ब्लॉक के अंदर ट्रांज़ैक्शन.ऑटोमिक का उपयोग करना है: "ट्राय: ट्रांजैक्शन.ऑटोमिक () के साथ: ।। को छोड़कर, अखंडता: ... "अब सब कुछ ठीक काम करता है।
caio

docs.djangoproject.com/en/dev/topics/db/transactions और उसके बाद "एक कोशिश में परमाणु को लपेटना / ब्लॉक को छोड़कर अखंडता त्रुटियों की प्राकृतिक हैंडलिंग के लिए अनुमति देता है:"
कैमहार्ट

जवाबों:


236

मैं खुद इसी समस्या में भाग गया। यह एक क्विक के कारण होता है कि कैसे लेन-देन को Django के नए संस्करणों में संभाला जाता है, जो एक ऐसे इरादे के साथ युग्मित होता है जो जानबूझकर एक अपवाद को ट्रिगर करता है।

मेरे पास एक ऐसा नास्तिक था जो यह सुनिश्चित करने के लिए जाँच करता था कि एक अद्वितीय स्तंभ बाधा उद्देश्यपूर्ण रूप से एक अखंडता अपवाद ट्रिगर करके लागू की गई थी:

def test_constraint(self):
    try:
        # Duplicates should be prevented.
        models.Question.objects.create(domain=self.domain, slug='barks')
        self.fail('Duplicate question allowed.')
    except IntegrityError:
        pass

    do_more_model_stuff()

Django 1.4 में, यह ठीक काम करता है। हालांकि, Django 1.5 / 1.6 में, प्रत्येक परीक्षण एक लेनदेन में लिपटे हुए है, इसलिए यदि कोई अपवाद होता है, तो यह लेनदेन को तब तक तोड़ता है जब तक आप स्पष्ट रूप से इसे वापस रोल नहीं करते। इसलिए, उस लेनदेन में कोई भी ORM संचालन, जैसे कि मेरा do_more_model_stuff(), उस django.db.transaction.TransactionManagementErrorअपवाद के साथ विफल हो जाएगा ।

टिप्पणियों में उल्लिखित caio की तरह, समाधान के साथ अपने अपवाद को पकड़ने के लिए है transaction.atomic:

from django.db import transaction
def test_constraint(self):
    try:
        # Duplicates should be prevented.
        with transaction.atomic():
            models.Question.objects.create(domain=self.domain, slug='barks')
        self.fail('Duplicate question allowed.')
    except IntegrityError:
        pass

यह उद्देश्यपूर्ण रूप से फेंके गए अपवाद को पूरे यूनिटेस्ट के लेन-देन को तोड़ने से रोक देगा।


70
इसके अलावा सिर्फ टेस्टकैश की बजाय अपने टेस्ट क्लास को ट्रांजेक्शनटेस्ट घोषित करें।
मोकिस्टीन

1
ओह, मुझे दूसरे प्रश्न से संबंधित दस्तावेज मिला । दस्तावेज़ यहाँ है
योबिन

2
मेरे लिए, मेरे पास पहले से ही एक transaction.atomic()ब्लॉक था, लेकिन मुझे यह त्रुटि मिली और मुझे पता नहीं क्यों था। मैंने इस उत्तर की सलाह ली और मुसीबत-क्षेत्र के चारों ओर अपने परमाणु ब्लॉक के अंदर एक नेस्टेड परमाणु ब्लॉक लगा दिया । उसके बाद, उसने मुझे दी गई अखंडता त्रुटि की एक विस्तृत त्रुटि दी, जिससे मुझे अपना कोड ठीक करने में मदद मिली और जो मैं करने की कोशिश कर रहा था।
19

5
@mkoistinen TestCaseको विरासत में मिला है TransactionTestCaseइसलिए इसे बदलने की कोई आवश्यकता नहीं है। यदि आप परीक्षण उपयोग में DB पर काम नहीं करते हैं SimpleTestCase
bns

1
@ आप टिप्पणी के बिंदु को याद कर रहे हैं। हां से TestCaseविरासत में मिला है TransactionTestCaseलेकिन इसका व्यवहार काफी अलग है: यह प्रत्येक परीक्षण पद्धति को एक लेनदेन में लपेटता है। TransactionTestCaseदूसरी ओर, शायद इसे भ्रामक रूप से नाम दिया गया है: यह db को रीसेट करने के लिए तालिकाओं को काटता है - नामकरण यह दर्शाता है कि आप एक परीक्षण के भीतर लेनदेन का परीक्षण कर सकते हैं, न कि परीक्षण को लेनदेन के रूप में लपेटा गया है!
सीएस

48

चूँकि @mkoistinen ने कभी अपनी टिप्पणी नहीं की , एक जवाब, मैं उनके सुझाव को पोस्ट करूँगा ताकि लोगों को टिप्पणियों के माध्यम से खुदाई न करनी पड़े।

केवल TestCase के बजाय अपने टेस्ट क्लास को TransactionTestCase घोषित करने पर विचार करें।

से डॉक्स : एक TransactionTestCase प्रतिबद्ध फोन और रोलबैक और डेटाबेस पर इन कॉल के प्रभाव देख सकते हैं।


2
+1 इसके लिए, लेकिन, जैसा कि डॉक्स कहते हैं, "Django का TestCase क्लास, TransactionTestCase का अधिक सामान्यतः उपयोग किया जाने वाला उपवर्ग है"। मूल प्रश्न का उत्तर देने के लिए, क्या हमें TestCase के बजाय SimpleTestCase का उपयोग नहीं करना चाहिए? SimpleTestCase में परमाणु डेटाबेस सुविधाएँ नहीं हैं।
डेगोरोकूब

@daigorocub जब से विरासत में मिला है SimpleTestCase, allow_database_queries = Trueतो परीक्षण वर्ग के अंदर जोड़ा जाना चाहिए, इसलिए यह थूक नहीं करता है AssertionError("Database queries aren't allowed in SimpleTestCase...",)
CristiFati

यह वह उत्तर है जो मेरे लिए सबसे अच्छा काम करता है क्योंकि मैं अखंडता के लिए परीक्षण करने की कोशिश कर रहा था और फिर बाद में मुझे और अधिक डेटाबेस बचाने के प्रश्नों की जरूरत थी
किम स्टैक

8

यदि pytest-django का उपयोग कर आप इस त्रुटि से बचने के transaction=Trueलिए django_dbडेकोरेटर को पास कर सकते हैं ।

Https://pytest-django.readthedocs.io/en/latest/database.html#testing-ransactions देखें

Django के पास खुद TransactionTestCase है जो आपको लेनदेन का परीक्षण करने की अनुमति देता है और उन्हें अलग करने के लिए परीक्षण के बीच डेटाबेस को फ्लश करेगा। इसका नकारात्मक पक्ष यह है कि ये परीक्षण डेटाबेस के आवश्यक फ्लशिंग के कारण स्थापित करने के लिए बहुत धीमे हैं। pytest-django भी परीक्षणों की इस शैली का समर्थन करता है, जिसे आप django_db मार्क के तर्क का उपयोग करके चुन सकते हैं:

@pytest.mark.django_db(transaction=True)
def test_spam():
    pass  # test relying on transactions

मेरे पास इस समाधान के साथ एक मुद्दा था, मेरे डीबी में प्रारंभिक डेटा था (माइग्रेशन द्वारा जोड़ा गया)। यह समाधान डेटाबेस को फ्लश करता है, इसलिए इस प्रारंभिक डेटा पर निर्भर अन्य परीक्षण विफल होने लगे।
abumalick

1

मेरे लिए, प्रस्तावित फ़िक्स ने काम नहीं किया। अपने परीक्षणों में, मैं कुछ उपप्रकारों Popenको विश्लेषण / लिंट माइग्रेशन (उदाहरण के लिए कोई मॉडल परिवर्तन नहीं होने पर एक परीक्षण जाँच) के साथ खोलता हूं ।

मेरे लिए, उपहास SimpleTestCaseकरने के बजाय टोटका TestCaseकिया।

ध्यान दें कि SimpleTestCaseडेटाबेस का उपयोग करने की अनुमति नहीं है।

हालांकि यह मूल प्रश्न का उत्तर नहीं देता है, मुझे आशा है कि यह कुछ लोगों को वैसे भी मदद करता है।


1

इस प्रश्न के उत्तर के आधार पर, इसे करने का एक और तरीका है:

with transaction.atomic():
    self.assertRaises(IntegrityError, models.Question.objects.create, **{'domain':self.domain, 'slug':'barks'})

0

मुझे django 1.9.7 का उपयोग करके अपने create_test_data फ़ंक्शन में इकाई परीक्षण चलाने पर यह त्रुटि मिल रही थी। इसने django के पुराने संस्करणों में काम किया।

ऐसा लग रहा था:

cls.localauth,_ = Organisation.objects.get_or_create(organisation_type=cls.orgtypeLA, name='LA for test', email_general='test@test.com', address='test', postcode='test', telephone='test')
cls.chamber,_ = Organisation.objects.get_or_create(organisation_type=cls.orgtypeC, name='chamber for test', email_general='test@test.com', address='test', postcode='test', telephone='test')
cls.lawfirm,_ = Organisation.objects.get_or_create(organisation_type=cls.orgtypeL, name='lawfirm for test', email_general='test@test.com', address='test', postcode='test', telephone='test')

cls.chamber.active = True
cls.chamber.save()

cls.localauth.active = True
cls.localauth.save()    <---- error here

cls.lawfirm.active = True
cls.lawfirm.save()

मेरा समाधान इसके बजाय update_or_create का उपयोग करना था:

cls.localauth,_ = Organisation.objects.update_or_create(organisation_type=cls.orgtypeLA, name='LA for test', email_general='test@test.com', address='test', postcode='test', telephone='test', defaults={'active': True})
cls.chamber,_ = Organisation.objects.update_or_create(organisation_type=cls.orgtypeC, name='chamber for test', email_general='test@test.com', address='test', postcode='test', telephone='test', defaults={'active': True})
cls.lawfirm,_ = Organisation.objects.update_or_create(organisation_type=cls.orgtypeL, name='lawfirm for test', email_general='test@test.com', address='test', postcode='test', telephone='test', defaults={'active': True})

1
get_or_create()के रूप में अच्छी तरह से काम करता है, ऐसा लगता है कि यह .save () यह एक लेनदेन के अंदर की तरह नहीं है। परमाणु () सजाया समारोह (मेरा वहाँ में सिर्फ 1 कॉल के साथ विफल)।
टिमोथी मकोबू

0

मैं एक ही मुद्दा है, लेकिन with transaction.atomic()और TransactionTestCaseमेरे लिए काम नहीं किया।

python manage.py test -rइसके बजाय python manage.py testमेरे लिए ठीक है, शायद निष्पादन का क्रम महत्वपूर्ण है

तब मुझे ऑर्डर के बारे में एक दस्तावेज मिलता है जिसमें परीक्षण निष्पादित किए जाते हैं , इसमें उल्लेख किया गया है कि कौन सा परीक्षण पहले चलेगा।

इसलिए, मैं unittest.TestCaseअन्य साधारण परीक्षण के लिए डेटाबेस इंटरेक्शन के लिए टेस्टकेस का उपयोग करता हूं , यह अब काम करता है!


0

@Kdazzle का उत्तर सही है। मैंने इसे करने की कोशिश नहीं की क्योंकि लोगों ने कहा कि 'Django का TestCase क्लास TransactionTestCase का एक अधिक सामान्यतः इस्तेमाल किया जाने वाला उपवर्ग है' इसलिए मैंने सोचा कि यह एक या एक ही उपयोग था। लेकिन जाहोंगिर रहमोनोव के ब्लॉग ने इसे बेहतर बताया:

TestCase वर्ग दो नेस्टेड परमाणु () ब्लॉकों के भीतर परीक्षणों को लपेटता है: पूरे वर्ग के लिए एक और प्रत्येक परीक्षण के लिए एक। यह वह जगह है जहां TransactionTestCase का उपयोग किया जाना चाहिए। यह परमाणु () ब्लॉक के साथ परीक्षणों को लपेटता नहीं है और इस प्रकार आप अपने विशेष तरीकों का परीक्षण कर सकते हैं जिन्हें बिना किसी समस्या के लेनदेन की आवश्यकता होती है।

संपादित करें: यह काम नहीं किया, मुझे लगता है हाँ, लेकिन नहीं।

4 साल में वे यह तय कर सकते थे ………………………………।


0
def test_wrong_user_country_db_constraint(self):
        """
        Check whether or not DB constraint doesnt allow to save wrong country code in DB.
        """
        self.test_user_data['user_country'] = 'XX'
        expected_constraint_name = "country_code_within_list_of_countries_check"

        with transaction.atomic():
            with self.assertRaisesRegex(IntegrityError, expected_constraint_name) as cm:
                get_user_model().objects.create_user(**self.test_user_data)

        self.assertFalse(
            get_user_model().objects.filter(email=self.test_user_data['email']).exists()
        )
with transaction.atomic() seems do the job correct

-4

मेरी भी यही समस्या थी।

माय केस में मैं यह कर रहा था

author.tasks.add(tasks)

इसलिए इसे परिवर्तित करना

author.tasks.add(*tasks)

उस त्रुटि को दूर किया।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.