You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django异步视图报错:未返回HttpResponse,返回未等待协程

问题排查与解决建议

错误原因

报错network_config_edit_view didn't return an HttpResponse object. It returned an unawaited coroutine的核心原因是:

  1. 异步视图被当作同步函数执行,Django未正确处理协程的执行流程,直接返回了协程对象而非HttpResponse。
  2. 异步视图中直接调用了同步函数(如admin.site.each_context(request)),未进行异步包装,导致上下文执行异常。

解决步骤

1. 启用Django异步视图支持

  • 使用ASGI服务器(推荐):
    切换到ASGI运行方式,使用uvicorn或daphne作为服务器,修改项目根目录的asgi.py确保正确启动:

    import os
    from django.core.asgi import get_asgi_application
    
    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'your_project.settings')
    application = get_asgi_application()
    

    启动命令示例:uvicorn your_project.asgi:application --host 0.0.0.0 --port 8000

  • 保持WSGI运行(兼容方案):
    在settings.py中添加配置,让Django自动重写异步视图为同步兼容形式:

    ASYNC_VIEW_REWRITE_ENABLED = True
    

2. 包装异步视图中的所有同步函数

异步视图中不能直接调用同步IO或阻塞函数,必须用sync_to_async包装:

修改admin.site.each_context的调用

所有使用admin.site.each_context(request)的地方,都要改为异步包装调用:

# 替换原来的**admin.site.each_context(request)
admin_context = await sync_to_async(admin.site.each_context, thread_sensitive=True)(request)
context = {
    'form': form,
    **admin_context,
}

包装render_to_string函数

render_to_string是同步函数,同样需要包装:

html = await sync_to_async(render_to_string, thread_sensitive=True)('admin/network_config_edit.html', context)

3. 完整修改后的视图代码片段

以下是关键部分修改后的示例:

async def network_config_edit_view(request):
    path = '/etc/netplan/00-network.yaml'
    backup_path = '/etc/netplan/00-network.yaml.backup'

    www_data_uid = 33
    www_data_gid = 33

    try:
        if await sync_to_async(os.path.exists, thread_sensitive=True)(backup_path):
            await sync_to_async(os.remove, thread_sensitive=True)(backup_path)
        await sync_to_async(shutil.copy, thread_sensitive=True)(path, backup_path)
        print(f"Backup of the original file created at {backup_path}.")
    except Exception as e:
        print(f"Error creating backup file: {e}")

    config_data = await sync_to_async(read_network_config, thread_sensitive=True)(path)
    if request.method == 'POST':
        form = NetworkConfigForm(request.POST)
        if form.is_valid():
            cleaned_data = form.cleaned_data
            data_to_save = {
                'ipv4_addresses': [cleaned_data.get('ipv4_address')] if cleaned_data.get('ipv4_address') else [],
                'ipv6_addresses': [cleaned_data.get('ipv6_address')] if cleaned_data.get('ipv6_address') else [],
                'ipv4_gateway': cleaned_data.get('ipv4_gateway', ''),
                'ipv6_gateway': cleaned_data.get('ipv6_gateway', ''),
                'dns_addresses': [addr.strip() for addr in cleaned_data.get('ipv4_dns', '').split(',') if addr.strip()] +
                                 [addr.strip() for addr in cleaned_data.get('ipv6_dns', '').split(',') if addr.strip()]
            }
            try:
                await sync_to_async(save_to_yaml, thread_sensitive=True)(data_to_save, file_path=path)
                await sync_to_async(os.chown, thread_sensitive=True)(path, www_data_uid, www_data_gid)
                await sync_to_async(os.chmod, thread_sensitive=True)(path, 0o664)
                print("YAML file has been saved.")
                messages.success(request, "The new configuration has been saved successfully.")
            except Exception as e:
                print(f"Error saving YAML file: {e}")
                messages.error(request, f"Error saving YAML file: {e}")
                # 修改admin_context调用
                admin_context = await sync_to_async(admin.site.each_context, thread_sensitive=True)(request)
                context = {
                    'form': form,
                    **admin_context,
                    'error': f"Error saving YAML file: {e}",
                }
                # 包装render_to_string
                html = await sync_to_async(render_to_string, thread_sensitive=True)('admin/network_config_edit.html', context)
                return HttpResponse(html)

        
            if await apply_netplan():
                
                if await check_network_status():
                    
                    await sync_to_async(restart_services, thread_sensitive=True)()
                    messages.success(request, "Services restarted successfully.")
                else:
                    messages.error(request, "Network check failed after applying netplan.")
                    admin_context = await sync_to_async(admin.site.each_context, thread_sensitive=True)(request)
                    context = {
                        'form': form,
                        **admin_context,
                        'error': "Network check failed after applying netplan.",
                    }
                    html = await sync_to_async(render_to_string, thread_sensitive=True)('admin/network_config_edit.html', context)
                    return HttpResponse(html)
            else:
                messages.error(request, "Failed to apply netplan.")
                admin_context = await sync_to_async(admin.site.each_context, thread_sensitive=True)(request)
                context = {
                    'form': form,
                    **admin_context,
                    'error': "Failed to apply netplan.",
                }
                html = await sync_to_async(render_to_string, thread_sensitive=True)('admin/network_config_edit.html', context)
                return HttpResponse(html)

    else:
        form = NetworkConfigForm(initial=config_data)

    admin_context = await sync_to_async(admin.site.each_context, thread_sensitive=True)(request)
    context = {
        'form': form,
        **admin_context,
    }
    html = await sync_to_async(render_to_string, thread_sensitive=True)('admin/network_config_edit.html', context)
    return HttpResponse(html)

额外注意事项

  • 确保www-data用户有执行sudo netplan apply和sudo systemctl restart gunicorn的权限,可通过编辑sudoers文件配置免密码执行:
    www-data ALL=(ALL) NOPASSWD: /usr/sbin/netplan apply, /bin/systemctl restart gunicorn
    
  • check_network_status中的ping命令可能因系统权限或防火墙规则失败,可替换为curl -I -m 5 google.com等更通用的网络检测命令。

内容的提问来源于stack exchange,提问作者Ruben San Martin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 16:15:56