当前位置: 首页 > 知识库问答 >
问题:

TransactionManagementError“在使用信号时,直到“原子”块结束时才能执行查询”,但只能在单元测试期间执行

琴镜
2023-03-14

在试图保存Django用户模型实例时,我遇到TransactionManagementError,在它的post_save信号中,我正在保存一些将用户作为外键的模型。

上下文和错误与使用信号时的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()

信号派克

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

测验。派克

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.

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

共有3个答案

闻人英韶
2023-03-14

如果使用pytest-django,您可以将交易=True传递给django_db装饰器以避免此错误。

看https://pytest-django.readthedocs.io/en/latest/database.html#testing-transactions

Django本身有TransactionTestCase,它允许您测试事务,并将在测试之间刷新数据库以隔离它们。这样做的缺点是,由于需要刷新数据库,这些测试的设置速度要慢得多。pytest django还支持这种类型的测试,您可以使用django_db标记的参数来选择:

@pytest.mark.django_db(transaction=True)
def test_spam():
    pass  # test relying on transactions
翟越
2023-03-14

因为@mkoistinen从来没有回答过他的评论,所以我将发布他的建议,这样人们就不必在评论中挖掘。

只需将测试类声明为事务测试用例,而不只是测试用例。

从Django文档中:TransactionTestCase可以调用提交和回滚,并观察这些调用对数据库的影响。

叶晋
2023-03-14

我自己也遇到了同样的问题。这是由于Django的较新版本中处理事务的方式有点奇怪,再加上故意触发异常的unittest造成的。

我有一个unittest,它检查以确保通过故意触发IntegrityError异常来强制执行唯一列约束:

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操作,如mydo\u more\u model\u stuff(),都将因该django而失败。db。交易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

这将防止故意抛出的异常破坏整个unittest的事务。

 类似资料:
  • 问题内容: 我在尝试保存Django用户模型实例时收到TransactionManagementError,并在其post_save信号中保存了一些将用户作为外键的模型。 使用信号时,上下文和错误与此问题django TransactionManagementError非常相似 但是,在这种情况下,错误仅在单元测试时发生。 它在手动测试中效果很好,但是单元测试失败。 有什么我想念的吗? 以下是代码

  • 问题内容: 尝试保存Django用户模型实例时,出现了TransactionManagementError,并且在其post_save信号中,我正在保存一些将用户作为外键的模型。 使用信号时,上下文和错误与此问题djangoTransactionManagementError]非常相似 但是,在这种情况下,错误 仅在单元测试 时发生。 它在手动测试中效果很好,但是单元测试失败。 有什么我想念的吗?

  • 所以,我的问题是,我正在尝试为我的应用程序进行单元测试。我有两个服务,我们叫它们Foo和Bar,Foo只是Bar的代理。 因此,Foo服务的路由器如下所示: 向Bar服务发出请求的处理程序如下所示: 当我这样写测试时: 它永远冻结了......嗯,我想这是因为它周围的一些协程魔法,但是因为我在这方面还是新手,我不明白这里到底发生了什么。有什么帮助吗?

  • 问题内容: 我有一个http服务器(使用启动),我想做一些操作。 我该怎么做(在Linux上)?在ctrl-C的情况下可以进行那些操作吗? 我不熟悉Unix信号,因此答案可能很简单。 问题答案: 您可以使用信号包订购TERM和INT信号。但是请注意,只有在明确终止进程时才发送这些信号。正常退出(由流程本身启动)不涉及任何信号。我认为,对于正常退出,只需在主例程中执行某些操作即可(该例程应该生成工作

  • 问题内容: 我想找出在Linux上用C ++编写的函数的执行时间。我发现了很多与此有关的帖子。我尝试了此链接“ 计时器方法”中提到的所有方法来计算时间。以下是我的函数执行时间的结果: 请帮助我哪种方法读数可靠,因为所有结果的读数都不同。我读到您的操作系统正在不同的任务之间切换,因此不能期望读数非常准确。有没有一种方法可以计算CPU在功能上花费的时间。我听说过使用概要分析工具,但是还没有找到仅用于函

  • 问题内容: 我有一个以datetime为参数的查询,我们观察到的是,如果通过变量提供datetime参数,则执行查询的时间比直接对参数进行硬编码要多2 -3倍,是否有任何原因或解决方案?对此 以下查询大约需要5分钟才能返回结果 虽然作为 它会在10到20秒内返回 我并不总是希望在列上使用索引进行搜索。 按照kevchadders的建议,我看到执行计划有很大的不同。使用日期变量的查询正在执行聚集索引