Flask-admin не может импортировать имя _class_resolver под uWSGI и Nginx
Я размещал проект Python Flask в AWS с использованием uWSGI и Nginx, изначально все работало нормально, но затем я решил внедрить модуль 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
Поиск в Google Я заметил, что ImportError может быть вызван из-за избыточного импорта, но я думаю, что необходим весь импорт flask-admin.
Я думаю, что проблема не вызвана 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 напрямую, все работает, как ожидалось, даже с импортом из-под flashk-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;
}
}
Большое спасибо за вашу помощь.
1 ответ
Я начну с немедленной ошибки, а затем рассмотрю причины, по которым она может отличаться от uWSGI.
Немедленная ошибка:
Здесь происходит небольшое ускорение импорта, что является обычным явлением, когда вы что-то создаете или используете функции. Хорошее место для начала - очистить его, чтобы уменьшить избыточность и очистить его. Даже если вы не решите проблему в пути, каждое удаление будет легче продумывать оставшийся импорт.
В дополнение к изменениям, предложенным Луисом в комментариях, вы можете объединить следующие две строки, которые используют разные соглашения для импорта из одного места:
Импортировать flask_admin как администратор из 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. Если вы уже используете Pylint, вы можете дважды проверить ваши настройки и убедиться, что правила, связанные с импортом, включены! Если вы еще не используете его, вы можете запустить его вручную из командной строки или использовать его через плагин редактора, который может запускать ваш код во время работы (вероятно, вы столкнетесь со многими проблемами, если вы еще этого не сделали). linting - но я бы просто сосредоточился на кодах импорта / модуля, чтобы начать).
Вы также можете найти это быстрее с помощью более сфокусированного инструмента, такого как pycycle, хотя я еще сам не попробовал.
Иногда вы сможете избежать проблем циклического импорта во Flask с помощью тщательного проектирования или небольшого рефакторинга, но по мере роста сложности вашего приложения новые функции с большей вероятностью приведут вас к циклическому импорту. Если вы выявили проблему, но не нашли очевидного пути ее решения, следующим шагом, как правило, является принятие шаблона фабрики приложений Flask. Этот паттерн не волшебен - вы все равно можете нарисовать себя в углах, связанных с импортом, - но он, как правило, облегчает их рефакторинг.
Запуск от uWSGI:
На данный момент, я думаю, что это две наиболее вероятные причины, по которым вы будете видеть другое поведение при запуске через python из командной строки и из-за uWSGI. Это просто общий список, поскольку более конкретный ответ зависит от нескольких вещей, помимо файлов, которые вы включили. У uWSGI и nginx есть довольно сложный набор параметров конфигурации, и я вылетаю из памяти, поэтому я могу быть не совсем точен...:)
Приложение настроено условно, возможно, с использованием переменных среды или что-то вроде
if __name__ == '__main__':
Таким образом, вы не запускаете точно один и тот же код в обоих случаях.uWSGI не использует тот же вызываемый / модуль. Между файлом конфигурации и командной строкой, я помню, было несколько способов сообщить uWSGI, какой файл python загружать и как называется объект python приложения, но я не помню, чтобы это можно было сделать из nginx., Я не вижу ни одного из них, указанных в вашем файле конфигурации, но включенный вами фрагмент журнала предполагает, что исключение импорта происходит, когда uWSGI пытается найти вызываемый объект (обычно
app
или жеapplication
) вrun.py
, Вы используете тот же код из CLI?