将 LoginRequiredMixin 与异步视图一起使用?

问题描述

我使用的是 Python 3.7、Django 3.1.7、social-auth-app-django 3.1.0。

我刚刚开始玩异步视图。我不能让他们用 LoginrequiredMixin 玩得很好。我有这个微不足道的看法:

class TestView(LoginrequiredMixin,View):
    @classonlymethod
    def as_view(cls,**initkwargs):
        view = super().as_view(**initkwargs)
        view._is_coroutine = asyncio.coroutines._is_coroutine
        return view

    async def get(self,request):
        return render(request,'spi/test.jinja',{})

当我运行这个测试时:

class TestViewTest(ViewTestCase):
    def test_get(self):
        client = Client()
        response = client.get('/spi/test')
        self.assertEqual(response.status_code,200)

它以令人敬畏的堆栈转储失败(如下),其要点是:

TypeError: object HttpResponseRedirect can't be used in 'await' expression

我认为这是由于正在运行的 OAuth 流程。如果我省略 LoginrequiredMixin,则测试通过。

--- Logging error ---
Traceback (most recent call last):
  File "/mnt/nfs/labstore-secondary-tools-project/spi-tools-dev/www/python/venv/lib/python3.7/site-packages/django/core/handlers/exception.py",line 47,in inner
    response = get_response(request)
  File "/mnt/nfs/labstore-secondary-tools-project/spi-tools-dev/www/python/venv/lib/python3.7/site-packages/django/core/handlers/base.py",line 181,in _get_response
    response = wrapped_callback(request,*callback_args,**callback_kwargs)
  File "/mnt/nfs/labstore-secondary-tools-project/spi-tools-dev/www/python/venv/lib/python3.7/site-packages/asgiref/sync.py",line 147,in __call__
    return call_result.result()
  File "/data/project/spi-tools-dev/python-distros/python-3.7.3-install/lib/python3.7/concurrent/futures/_base.py",line 425,in result
    return self.__get_result()
  File "/data/project/spi-tools-dev/python-distros/python-3.7.3-install/lib/python3.7/concurrent/futures/_base.py",line 384,in __get_result
    raise self._exception
  File "/mnt/nfs/labstore-secondary-tools-project/spi-tools-dev/www/python/venv/lib/python3.7/site-packages/asgiref/sync.py",line 212,in main_wrap
    result = await self.awaitable(*args,**kwargs)
TypeError: object HttpResponseRedirect can't be used in 'await' expression

During handling of the above exception,another exception occurred:

Traceback (most recent call last):
  File "/data/project/spi-tools-dev/python-distros/python-3.7.3-install/lib/python3.7/logging/handlers.py",line 630,in emit
    s = self.makePickle(record)
  File "/data/project/spi-tools-dev/python-distros/python-3.7.3-install/lib/python3.7/logging/handlers.py",line 602,in makePickle
    s = pickle.dumps(d,1)
  File "/data/project/spi-tools-dev/python-distros/python-3.7.3-install/lib/python3.7/copyreg.py",line 65,in _reduce_ex
    raise TypeError("can't pickle %s objects" % base.__name__)
TypeError: can't pickle BytesIO objects
Call stack:
  File "/data/project/spi-tools-dev/www/python/src/manage.py",line 25,in <module>
    main()
  File "/data/project/spi-tools-dev/www/python/src/manage.py",line 21,in main
    execute_from_command_line(sys.argv)
  File "/mnt/nfs/labstore-secondary-tools-project/spi-tools-dev/www/python/venv/lib/python3.7/site-packages/django/core/management/__init__.py",line 401,in execute_from_command_line
    utility.execute()
  File "/mnt/nfs/labstore-secondary-tools-project/spi-tools-dev/www/python/venv/lib/python3.7/site-packages/django/core/management/__init__.py",line 395,in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/mnt/nfs/labstore-secondary-tools-project/spi-tools-dev/www/python/venv/lib/python3.7/site-packages/django/core/management/commands/test.py",line 23,in run_from_argv
    super().run_from_argv(argv)
  File "/mnt/nfs/labstore-secondary-tools-project/spi-tools-dev/www/python/venv/lib/python3.7/site-packages/django/core/management/base.py",line 330,in run_from_argv
    self.execute(*args,**cmd_options)
  File "/mnt/nfs/labstore-secondary-tools-project/spi-tools-dev/www/python/venv/lib/python3.7/site-packages/django/core/management/base.py",line 371,in execute
    output = self.handle(*args,**options)
  File "/mnt/nfs/labstore-secondary-tools-project/spi-tools-dev/www/python/venv/lib/python3.7/site-packages/django/core/management/commands/test.py",line 53,in handle
    failures = test_runner.run_tests(test_labels)
  File "/mnt/nfs/labstore-secondary-tools-project/spi-tools-dev/www/python/venv/lib/python3.7/site-packages/django/test/runner.py",line 699,in run_tests
    result = self.run_suite(suite)
  File "/mnt/nfs/labstore-secondary-tools-project/spi-tools-dev/www/python/venv/lib/python3.7/site-packages/django/test/runner.py",line 641,in run_suite
    return runner.run(suite)
  File "/data/project/spi-tools-dev/python-distros/python-3.7.3-install/lib/python3.7/unittest/runner.py",line 176,in run
    test(result)
  File "/data/project/spi-tools-dev/python-distros/python-3.7.3-install/lib/python3.7/unittest/suite.py",line 84,in __call__
    return self.run(*args,**kwds)
  File "/data/project/spi-tools-dev/python-distros/python-3.7.3-install/lib/python3.7/unittest/suite.py",line 122,in run
    test(result)
  File "/mnt/nfs/labstore-secondary-tools-project/spi-tools-dev/www/python/venv/lib/python3.7/site-packages/django/test/testcases.py",line 243,in __call__
    self._setup_and_call(result)
  File "/mnt/nfs/labstore-secondary-tools-project/spi-tools-dev/www/python/venv/lib/python3.7/site-packages/django/test/testcases.py",line 279,in _setup_and_call
    super().__call__(result)
  File "/data/project/spi-tools-dev/python-distros/python-3.7.3-install/lib/python3.7/unittest/case.py",line 663,**kwds)
  File "/data/project/spi-tools-dev/python-distros/python-3.7.3-install/lib/python3.7/unittest/case.py",line 615,in run
    testMethod()
  File "/mnt/nfs/labstore-secondary-tools-project/spi-tools-dev/www/python/src/spi/test_views.py",line 231,in test_get
    response = client.get('/spi/test')
  File "/mnt/nfs/labstore-secondary-tools-project/spi-tools-dev/www/python/venv/lib/python3.7/site-packages/django/test/client.py",line 739,in get
    response = super().get(path,data=data,secure=secure,**extra)
  File "/mnt/nfs/labstore-secondary-tools-project/spi-tools-dev/www/python/venv/lib/python3.7/site-packages/django/test/client.py",in get
    **extra,File "/mnt/nfs/labstore-secondary-tools-project/spi-tools-dev/www/python/venv/lib/python3.7/site-packages/django/test/client.py",line 470,in generic
    return self.request(**r)
  File "/mnt/nfs/labstore-secondary-tools-project/spi-tools-dev/www/python/venv/lib/python3.7/site-packages/django/test/client.py",line 711,in request
    response = self.handler(environ)
  File "/mnt/nfs/labstore-secondary-tools-project/spi-tools-dev/www/python/venv/lib/python3.7/site-packages/django/test/client.py",line 142,in __call__
    response = self.get_response(request)
  File "/mnt/nfs/labstore-secondary-tools-project/spi-tools-dev/www/python/venv/lib/python3.7/site-packages/django/core/handlers/base.py",line 130,in get_response
    response = self._middleware_chain(request)
  File "/mnt/nfs/labstore-secondary-tools-project/spi-tools-dev/www/python/venv/lib/python3.7/site-packages/django/core/handlers/exception.py",in inner
    response = get_response(request)
  File "/mnt/nfs/labstore-secondary-tools-project/spi-tools-dev/www/python/venv/lib/python3.7/site-packages/django/utils/deprecation.py",line 114,in __call__
    response = response or self.get_response(request)
  File "/mnt/nfs/labstore-secondary-tools-project/spi-tools-dev/www/python/venv/lib/python3.7/site-packages/django/core/handlers/exception.py",in inner
    response = get_response(request)
  File "/mnt/nfs/labstore-secondary-tools-project/spi-tools-dev/www/python/venv/lib/python3.7/site-packages/debug_toolbar/middleware.py",line 52,in __call__
    return self.get_response(request)
  File "/mnt/nfs/labstore-secondary-tools-project/spi-tools-dev/www/python/venv/lib/python3.7/site-packages/django/core/handlers/exception.py",in inner
    response = get_response(request)
  File "/mnt/nfs/labstore-secondary-tools-project/spi-tools-dev/www/python/src/tools_app/middleware.py",line 26,line 11,line 49,in inner
    response = response_for_exception(request,exc)
  File "/mnt/nfs/labstore-secondary-tools-project/spi-tools-dev/www/python/venv/lib/python3.7/site-packages/django/core/handlers/exception.py",line 108,in response_for_exception
    exc_info=sys.exc_info(),File "/mnt/nfs/labstore-secondary-tools-project/spi-tools-dev/www/python/venv/lib/python3.7/site-packages/django/utils/log.py",line 230,in log_response
    exc_info=exc_info,Message: '%s: %s'
Arguments: ('Internal Server Error','/spi/test')
E
======================================================================
ERROR: test_get (spi.test_views.TestViewTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/mnt/nfs/labstore-secondary-tools-project/spi-tools-dev/www/python/src/spi/test_views.py",line 716,in request
    self.check_exception(response)
  File "/mnt/nfs/labstore-secondary-tools-project/spi-tools-dev/www/python/venv/lib/python3.7/site-packages/django/test/client.py",line 577,in check_exception
    raise exc_value
  File "/mnt/nfs/labstore-secondary-tools-project/spi-tools-dev/www/python/venv/lib/python3.7/site-packages/django/core/handlers/exception.py",**kwargs)
TypeError: object HttpResponseRedirect can't be used in 'await' expression

----------------------------------------------------------------------
Ran 1 test in 0.274s

Failed (errors=1)
Destroying test database for alias 'default'...

解决方法

好吧,事实证明问题很简单,LoginRequiredMixin 还不能异步。解决方法是在我的视图代码中实现 LoginRequiredMixin 的要点。 supplied implementation of LoginRequiredMixin 很复杂,但大部分是处​​理一般情况。将其分解为适合我的用例的基本部分,我最终得到:

from asgiref.sync import sync_to_async
from django.contrib.auth.views import redirect_to_login

class TestView(View):
    @classonlymethod
    def as_view(cls,**initkwargs):
        view = super().as_view(**initkwargs)
        view._is_coroutine = asyncio.coroutines._is_coroutine
    return view


    async def get(self,request):
        if not await sync_to_async(lambda: request.user.is_authenticated)():
            return redirect_to_login(self.request.get_full_path())
        return render(request,'spi/test.jinja',{})