【问题标题】:Flask-admin cannot import name _class_resolver, under uWSGI and NginxFlask-admin 无法导入名称 _class_resolver,在 uWSGI 和 Nginx 下
【发布时间】:2018-04-23 17:32:07
【问题描述】:

我使用 uWSGINginxAWS 中托管了一个 python Flask 项目,最初一切正常,但后来我决定实现 Flask-admin 模块。现在页面向我显示了内部服务器错误消息。

我检查了 UWSGI 日志并说:

  Traceback (most recent call last):
  File "/var/www/project/run.py", line 2, in <module>
    from app import app
  File "/var/www/project/app/__init__.py", line 22, in <module>
    from app import views
  File "/var/www/project/app/views.py", line 15, in <module>
    from flask_admin.contrib.sqla import ModelView
  File "/var/www/project/venv/local/lib/python2.7/site-packages/flask_admin/contrib/sqla/__init__.py", line 2, in <module>
    from .view import ModelView
  File "/var/www/project/venv/local/lib/python2.7/site-packages/flask_admin/contrib/sqla/view.py", line 17, in <module>
    from flask_admin.contrib.sqla.tools import is_relationship
  File "/var/www/project/venv/local/lib/python2.7/site-packages/flask_admin/contrib/sqla/tools.py", line 4, in <module>
    from sqlalchemy.ext.declarative.clsregistry import _class_resolver
ImportError: cannot import name _class_resolver
Fri Nov 10 07:41:08 2017 - unable to load app 0 (mountpoint='project.domain.com|') (callable not found or import error)
Fri Nov 10 07:41:08 2017 - --- no python application found, check your startup logs for errors ---

我的 app/views.py 文件:

# -*- coding: utf-8 -*-
import os, time
import flask
from flask import render_template, request, jsonify, send_from_directory, send_file, session, redirect, g, url_for, make_response, session, request
from flask_sqlalchemy import SQLAlchemy
from models import RequestsLog
import requests
import json
from app import app, db, appname #Line edited
import flask_admin as admin
#from flask_admin import Admin, helpers, expose  ------Line Erased
from flask_admin.contrib.sqla import ModelView
#from flask_admin.contrib import sqla   ----------- Line Erased

class MyAdminIndexView(admin.AdminIndexView):
        @admin.expose('/')
        def index(self):
                count = 56
                return self.render('admin/index.html', count=count)

class CustomView(ModelView):
       can_create = False
       can_edit = False
       can_delete = False  # disable model deletion
       can_export = True
       export_columns = ['phone_number', 'doc_number', 'date', 'doc_type']
       can_view_details = True       
       column_export_exclude_list = ['job_id', 'status']
       list_template = 'list.html'
       details_modal = True
       page_size = 50
       can_set_page_size = True

admin = admin.Admin(app, appname, template_mode='bootstrap3', index_view=MyAdminIndexView())
admin.add_view(CustomView(RequestsLog, db.session, menu_icon_type ='fa', menu_icon_value='fa-calendar'))

编辑: 如果我评论这一行,一切都会正常运行:

from flask_admin.contrib.sqla import ModelView

谷歌搜索我注意到 ImportError 可能是由于冗余导入引起的,但我认为所有烧瓶管理导入都是必要的。

我认为问题不是由 uWSGI 引起的,但这里是配置文件:

[uwsgi]
uid = www-data
gid = www-data

plugins=python
vhost=true
socket=/tmp/project.sock
chmod-socket = 666
chown-socket = www-data:www-data

enable-threads = true
procname-prefix = project_
chdir = /var/www/project

我再次创建了虚拟环境,但一切都安装得很好。

编辑: 应该注意的是,当我直接执行 run.py 文件时,一切都按预期工作,即使使用 flask-admin 导入,问题就在下面uWSGI。

最后我的项目托管在 Ubuntu 14.04 Trusty AWS 依赖项中。

编辑:

添加run.py文件:

#!venv/bin/python
from app import app

if __name__ == '__main__':

    app.run(host='0.0.0.0', threaded=True, debug=True, port=5010)

编辑 2:

添加 __init__.py 文件:

# -*- coding: utf-8 -*-
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_socketio import SocketIO
#import flask_admin as admin
from flask_babel import Babel

#I've defined the next three lines to avoid encoding conflicts
import sys
reload(sys)
sys.setdefaultencoding("utf-8")

app = Flask(__name__)
app.config.from_pyfile('../config.py')
appname = 'Project'

db = SQLAlchemy(app, session_options={'autoflush': False})
babel = Babel(app)

async_mode = None
socketio = SocketIO(app, async_mode=async_mode, ping_timeout=1800)

from app import views

venv/bin/pip freeze 输出:

Babel==2.5.1
Flask==0.12.2
Flask-Admin==1.5.0
Flask-Babel==0.11.2
Flask-SQLAlchemy==2.3.2
Flask-SocketIO==2.9.2
Jinja2==2.9.6
MarkupSafe==1.0
SQLAlchemy==1.1.14
WTForms==2.1
Werkzeug==0.12.2
argparse==1.2.1
asn1crypto==0.22.0
certifi==2017.7.27.1
chardet==3.0.4
click==6.7
click-completion==0.2.1
colorama==0.3.9
crayons==0.1.2
elibom==1.2
gunicorn==19.7.1
idna==2.6
itsdangerous==0.24
psycopg2==2.7.3.1
py==1.4.34
pycycle==0.0.8
pytest==3.2.3
python-engineio==1.7.0
python-socketio==1.8.1
pytz==2017.3
requests==2.18.4
six==1.11.0
uWSGI==2.0.15
urllib3==1.22
wsgiref==0.1.2

Nginx 配置文件:

server {
        listen  80 ssl;
    server_name project.domain.com; 
    listen 443 ssl;

        ssl_certificate /etc/nginx/project.domain.com/fullchain.pem;
        ssl_certificate_key /etc/nginx/project.domain.com/privkey.pem;
        ssl_session_timeout 1d;
        ssl_session_cache shared:SSL:50m;

        ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS !MEDIUM !RC4";
        ssl_prefer_server_ciphers on;

        ssl_stapling on;
        ssl_stapling_verify on;

    location /static/ {
                autoindex               on;
                alias                   /var/www/project/app/static/;
        }

        location / {
                include                 uwsgi_params;
                uwsgi_pass              unix:/tmp/project.sock;
                uwsgi_read_timeout      300;
                uwsgi_param             UWSGI_PYHOME    /var/www/project/venv;
                uwsgi_param             UWSGI_CHDIR     /var/www/project;
                uwsgi_param             UWSGI_MODULE    run;
                uwsgi_param             UWSGI_CALLABLE  app;
        }

        error_page 404 /404.html;

        location /socket.io {
            include proxy_params;
            proxy_http_version 1.1;
            proxy_buffering off;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "Upgrade";
            proxy_pass http://127.0.0.1:5010/socket.io;
    }
}

非常感谢您的帮助。

【问题讨论】:

  • 如果只导入 sqla 并且在子类化 ModelView 时使用 sqla.ModelView 会发生什么?
  • 就此而言,至少在您粘贴的代码中,您没有在任何地方使用sqla,您确定需要导入吗?
  • @LuisOrduz 我已经完成了该测试,但问题仍然存在。甚至删除 sqla 导入。但是为什么当我直接运行 python 而不是通过 uwsgi 时它会起作用?
  • 澄清一下:在添加flask-admin之前,你让它通过python和uWSGI工作吗?你能包含 app.py 的代码吗?应用程序在直接通过 Python 运行时与在 uWSGI 后面运行时表现不同是很常见的(有时是可取的)。主应用文件通过python &lt;app&gt;.py运行时可以使用if __name__ == "__main__":设置一种方式,通过uWSGI运行时使用else块设置另一种方式。
  • @abathur pip freeze added,我没有app.py 文件,只有run.py

标签: python nginx flask uwsgi flask-admin


【解决方案1】:

我将从直接错误开始,然后解决它可能与 uWSGI 运行方式不同的原因。

立即错误:

这里有一些快速和松散的导入,这在您构建某些东西或添加功能时很常见。一个好的起点是削减它以减少冗余并清理它。即使您没有在途中解决问题,每次删除都会使剩余的导入更容易思考。

除了 Luis 在 cmets 中建议的更改之外,您还可以结合使用不同约定的以下两行从同一个地方导入:

以管理员身份导入 flask_admin from flask_admin 导入管理员、助手、暴露

您可以只导入 flask_admin 本身:

import flask_admin
...
class MyAdminIndexView(flask_admin.AdminIndexView):
    @flask_admin.expose('/')
...
admin = flask_admin.Admin...

或者您可以将AdminIndexView 添加到显式导入列表中:

from flask_admin import Admin, AdminIndexView, helpers, expose

我遇到的大多数 Flask 导入错误来自尝试从您的应用程序中导入另一个尚未正确初始化的对象(通常是由于 循环 导入),所以我我猜它潜伏在这 3 行之一的后面:

from app import app
...
from models import RequestsLog
...
from app import db, appname

原因可能会突如其来,但有时循环是几个模块的深度。如果没有,下一步是通过诸如Pylint 之类的 linter 运行您的项目。如果您已经在运行 Pylint,您可能需要仔细检查您的设置并确保启用了与导入相关的规则!如果您还没有使用它,您可以从命令行手动运行它,或者通过编辑器插件使用它,该插件可以在您工作时通过它运行您的代码(如果您还没有,它可能会抱怨很多问题) linting——但我只关注导入/模块相关的代码开始)。

您还可以使用pycycle 等更集中的工具更快地找出它,尽管我自己还没有尝试过。

有时您可以通过仔细设计或稍作重构来避免 Flask 中的循环导入问题,但随着应用程序复杂性的增加,新功能更有可能引导您进行循环导入。如果您发现问题但找不到明显的解决方法,下一步通常是采用 Flask application factory pattern。这种模式并不神奇——你仍然可以将自己描绘成导入问题的角落——但它确实倾向于使它们更容易重构出来。

从 uWSGI 运行:

目前,我认为这两个最有可能的原因是您在从命令行通过 python 运行和从 uWSGI 后面运行时会看到不同的行为。这只是一个通用列表,因为更具体的答案取决于您包含的文件之外的一些内容。 uWSGI 和 nginx 都有相当复杂的配置选项数组,我在这里是从内存中飞出来的,所以我可能并不完全准确...... :)

  1. 应用程序是有条件地设置的,可能使用环境变量或if __name__ == '__main__': 之类的东西,因此您在两种情况下运行的代码并不完全相同。

  2. uWSGI 没有使用相同的可调用/模块。在配置文件和命令行之间,我记得有几种方法可以告诉 uWSGI 要加载哪个 python 文件,以及应用程序的 python 对象的名称是什么,但我不记得能够从 nginx 这样做.我没有在您的配置文件中看到任何这些指定,但是您包含的日志 sn-p 表明导入异常发生在 uWSGI 试图在 run.py 中查找可调用对象(通常为 appapplication)时。您是否从 CLI 运行相同的代码?

【讨论】:

  • 嗨@abathur,您在第一段中提到的导入在这里使用class MyAdminIndexView(admin.AdminIndexView)。在测试您的建议之前,我有一个疑问,为什么当我直接运行 python 而不是通过 uwsgi 时它可以工作?
  • 糟糕——我都错过了。我会稍微更新当前的答案,以便稍后解决您的第一点。首先,我将评论您关于另一点的问题——我认为这对于该线程的其他访问者来说会更清楚。
  • 是的,我正在运行相同的代码,我的应用名为 app。我的 Nginx 文件包括以下几行:` uwsgi_param UWSGI_MODULE run; uwsgi_param UWSGI_CALLABLE app ``` 那是常用的nginx配置,要不要告诉我,我贴一下nginx完整配置文件。
  • 但问题是当我不包含 ModelView 时它确实在 uWSGI 下工作
  • 现在我尝试了pycycle,这是一个非常有用的模块(感谢您向我展示),但它显示:No worries, no cycles here!。我认为application factory pattern 应该用在更复杂的应用程序中。
猜你喜欢
  • 2019-05-01
  • 2019-05-01
  • 2015-12-24
  • 1970-01-01
  • 2018-08-10
  • 1970-01-01
  • 2014-02-01
  • 2019-06-03
  • 1970-01-01
相关资源
最近更新 更多