python - 使用线程时 Django 中的数据库错误

标签 python django multithreading postgresql

我在一个需要查询 PostgreSQL 数据库的 Django 网络应用程序中工作。使用 Python 实现并发时 threading界面,我收到查询项目的 DoesNotExist 错误。当然,按顺序执行查询时不会出现这些错误。

让我展示我编写的单元测试来演示意外行为:

class ThreadingTest(TestCase):
    fixtures = ['demo_city',]

    def test_sequential_requests(self):
        """
        A very simple request to database, made sequentially.

        A fixture for the cities has been loaded above. It is supposed to be
        six cities in the testing database now. We will made a request for
        each one of the cities sequentially.
        """
        for number in range(1, 7):
            c = City.objects.get(pk=number)
            self.assertEqual(c.pk, number)

    def test_threaded_requests(self):
        """
        Now, to test the threaded behavior, we will spawn a thread for
        retrieving each city from the database.
        """

        threads = []
        cities = []

        def do_requests(number):
            cities.append(City.objects.get(pk=number))

        [threads.append(threading.Thread(target=do_requests, args=(n,))) for n in range(1, 7)]

        [t.start() for t in threads]
        [t.join() for t in threads]

        self.assertNotEqual(cities, [])

如您所见,第一个测试按顺序执行了一些数据库请求,这些请求确实没有问题。然而,第二个测试执行完全相同的请求,但每个请求都在一个线程中生成。这实际上是失败的,返回一个 DoesNotExist 异常。

这个单元测试的执行输出是这样的:

test_sequential_requests (cesta.core.tests.threadbase.ThreadingTest) ... ok
test_threaded_requests (cesta.core.tests.threadbase.ThreadingTest) ...

Exception in thread Thread-1:
Traceback (most recent call last):
  File "/usr/lib/python2.6/threading.py", line 532, in __bootstrap_inner
    self.run()
  File "/usr/lib/python2.6/threading.py", line 484, in run
    self.__target(*self.__args, **self.__kwargs)
  File "/home/jose/Work/cesta/trunk/src/cesta/core/tests/threadbase.py", line 45, in do_requests
    cities.append(City.objects.get(pk=number))
  File "/home/jose/Work/cesta/trunk/parts/django/django/db/models/manager.py", line 132, in get
    return self.get_query_set().get(*args, **kwargs)
  File "/home/jose/Work/cesta/trunk/parts/django/django/db/models/query.py", line 349, in get
    % self.model._meta.object_name)
DoesNotExist: City matching query does not exist.

...其他线程返回类似的输出...

Exception in thread Thread-6:
Traceback (most recent call last):
  File "/usr/lib/python2.6/threading.py", line 532, in __bootstrap_inner
    self.run()
  File "/usr/lib/python2.6/threading.py", line 484, in run
    self.__target(*self.__args, **self.__kwargs)
  File "/home/jose/Work/cesta/trunk/src/cesta/core/tests/threadbase.py", line 45, in do_requests
    cities.append(City.objects.get(pk=number))
  File "/home/jose/Work/cesta/trunk/parts/django/django/db/models/manager.py", line 132, in get
    return self.get_query_set().get(*args, **kwargs)
  File "/home/jose/Work/cesta/trunk/parts/django/django/db/models/query.py", line 349, in get
    % self.model._meta.object_name)
DoesNotExist: City matching query does not exist.


FAIL

======================================================================
FAIL: test_threaded_requests (cesta.core.tests.threadbase.ThreadingTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/jose/Work/cesta/trunk/src/cesta/core/tests/threadbase.py", line 52, in test_threaded_requests
    self.assertNotEqual(cities, [])
AssertionError: [] == []

----------------------------------------------------------------------
Ran 2 tests in 0.278s

FAILED (failures=1)
Destroying test database for alias 'default' ('test_cesta')...

请记住,所有这一切都发生在 PostgreSQL 数据库中,它应该是线程安全的,而不是 SQLite 或类似的数据库。测试也使用 PostgreSQL 运行。

在这一点上,我完全不知道可能会失败的原因。有什么想法或建议吗?

谢谢!

编辑: 我写了一个小 View 只是为了检查它是否在测试中有效。这是 View 的代码:

def get_cities(request):
    queue = Queue.Queue()

    def get_async_cities(q, n):
        city = City.objects.get(pk=n)
        q.put(city)

    threads = [threading.Thread(target=get_async_cities, args=(queue, number)) for number in range(1, 5)]

    [t.start() for t in threads]
    [t.join() for t in threads]

    cities = list()

    while not queue.empty():
        cities.append(queue.get())

    return render_to_response('async/cities.html', {'cities': cities},
        context_instance=RequestContext(request))

(请不要考虑在 View 代码中编写应用程序逻辑的愚蠢行为。请记住,这只是概念验证,不会永远不会出现在实际应用中。)

结果是代码运行良好,请求在线程中成功发出, View 在调用其 URL 后最终显示城市。

因此,我认为使用线程进行查询只会在您需要测试代码时出现问题。在生产中,它将毫无问题地工作。

有什么有用的建议可以成功测试这种代码吗?

最佳答案

尝试使用 TransactionTestCase:

class ThreadingTest(TransactionTestCase):

TestCase 将数据保存在内存中并且不会向数据库发出 COMMIT。可能线程正在尝试直接连接到数据库,而数据尚未提交到那里。在这里查看说明: https://docs.djangoproject.com/en/dev/topics/testing/?from=olddocs#django.test.TransactionTestCase

TransactionTestCase and TestCase are identical except for the manner in which the database is reset to a known state and the ability for test code to test the effects of commit and rollback. A TransactionTestCase resets the database before the test runs by truncating all tables and reloading initial data. A TransactionTestCase may call commit and rollback and observe the effects of these calls on the database.

关于python - 使用线程时 Django 中的数据库错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10948537/

相关文章:

python - 在类定义之外引用实例方法

Python:如何处理 urllib2 中服务器抛出的异常?

python - 如何从扫描图像中裁剪黑角?

Django 查询相当于 MySQL GREATEST 函数?

python - 如何将静态参数传递给 python 线程池映射

python - pandas 日期时间格式转换

python - Ubuntu 导入错误 : Could not import settings (works on Mac)

Django mod-wsgi 错误 500 - 日志文件中没有任何用处

iphone - PerformselectorinBackground 不起作用

c# - 调用异步 ActionResults 时,AllowVoidAsyncOperations 何时设置?