Мое flask приложение, развернутое с Heroku, аварийно завершает работу, когда я изменяю базу данных на postgresql с sqllite.
Я настроил свое приложение flask, используя базу данных SQL Lite для локальной разработки. Первоначально мой DATABASE_URI в моем конфигурационном файле был следующим:
SQLALCHEMY_DATABASE_URI = "sqlite:///" + os.path.join(basedir, "app.db")
Я успешно развернул его в Heroku без ошибок. Но из-за эфемерной файловой системы Heroku я должен изменить свою базу данных с sqlite на postgresql.
Я добавил postgresql в Heroku и проверил URL, выполнив os.environ.get ('DATABASE_URL') в терминале Heroku Python. Я изменил свой DATABASE_URI на:
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or "sqlite:///" + os.path.join(basedir, "app.db")
Теперь при развертывании в Heroku выдается ошибка.
2020-03-06T15:54:29.068483+00:00 app[web.1]: LINE 1: PRAGMA foreign_keys=ON
2020-03-06T15:54:29.068484+00:00 app[web.1]: ^
2020-03-06T15:54:29.068484+00:00 app[web.1]:
2020-03-06T15:54:29.068484+00:00 app[web.1]:
2020-03-06T15:54:29.068485+00:00 app[web.1]: The above exception
was the direct cause of the following exception:
2020-03-06T16:01:19.714003+00:00 app[web.1]: pool.dispatch.connect(self.connection, self)
2020-03-06T16:01:19.714004+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/sqlalchemy/event/attr.py", line 320, in __call__
2020-03-06T16:01:19.714004+00:00 app[web.1]: fn(*args, **kw)
2020-03-06T16:01:19.714005+00:00 app[web.1]: File "/app/app/__init__.py", line 47, in set_sqlite_pragma
2020-03-06T16:01:19.714005+00:00 app[web.1]: cursor.execute("PRAGMA foreign_keys=ON")
2020-03-06T16:01:19.714006+00:00 app[web.1]: sqlalchemy.exc.ProgrammingError: (psycopg2.errors.SyntaxError) syntax error at or near "PRAGMA"
2020-03-06T16:01:19.714006+00:00 app[web.1]: LINE 1: PRAGMA foreign_keys=ON
2020-03-06T16:01:19.714006+00:00 app[web.1]: ^
2020-03-06T16:01:19.714007+00:00 app[web.1]:
2020-03-06T16:01:19.714007+00:00 app[web.1]: (Background on this
error at: http://sqlalche.me/e/f405)
2020-03-06T16:01:19.714912+00:00 app[web.1]: [2020-03-06 16:01:19 +0000] [16] [INFO] Worker exiting (pid: 16)
2020-03-06T16:01:19.853253+00:00 app[web.1]: [2020-03-06 16:01:19 +0000] [12] [INFO] Shutting down: Master
2020-03-06T16:01:19.853442+00:00 app[web.1]: [2020-03-06 16:01:19 +0000] [12] [INFO] Reason: Worker failed to boot.
2020-03-06T16:01:19.958297+00:00 heroku[web.1]: State changed from up to crashed
2020-03-06T16:01:19.940169+00:00 heroku[web.1]: Process exited with status 3
Вот мой полный config.py
import os
from dotenv import load_dotenv
basedir = os.path.abspath(os.path.dirname(__file__))
load_dotenv(os.path.join(basedir, '.env'))
class Config(object):
CSRF_ENABLED = True
SECRET_KEY = "youwillneverguess"
OPENID_PROVIDERS = [
{"name": "Google", "url": "https://www.google.com/accounts/o8/id"},
{"name": "Yahoo", "url": "https://me.yahoo.com"},
{"name": "AOL", "url": "http://openid.aol.com/<username>"},
{"name": "Flickr", "url": "http://www.flickr.com/<username>"},
{"name": "MyOpenID", "url": "https://www.myopenid.com"},
]
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or "sqlite:///" + os.path.join(basedir, "app.db")
#SQLALCHEMY_DATABASE_URI = "sqlite:///" + os.path.join(basedir, "app.db")
SQLALCHEMY_TRACK_MODIFICATIONS = False
BABEL_DEFAULT_LOCALE = "en"
BABEL_DEFAULT_FOLDER = "translations"
LANGUAGES = {
"en": {"flag": "gb", "name": "English"},
"pt": {"flag": "pt", "name": "Portuguese"},
"es": {"flag": "es", "name": "Spanish"},
"de": {"flag": "de", "name": "German"},
"zh": {"flag": "cn", "name": "Chinese"},
"ru": {"flag": "ru", "name": "Russian"},
}
# ------------------------------
# GLOBALS FOR GENERAL APP's
# ------------------------------
UPLOAD_FOLDER = basedir + "/app/static/uploads/"
IMG_UPLOAD_FOLDER = basedir + "/app/static/uploads/"
IMG_UPLOAD_URL = "/static/uploads/"
IMG_SIZE = (150, 150, True)
AUTH_TYPE = 1
AUTH_ROLE_ADMIN = "Admin"
AUTH_ROLE_PUBLIC = "Public"
APP_NAME = "Emily’s MDL1 Class"
# APP_THEME = "" # default
# APP_THEME = "cerulean.css" # COOL
# APP_THEME = "cerulean.css" # COOL
# APP_THEME = "amelia.css"
# APP_THEME = "cosmo.css"
# APP_THEME = "cyborg.css" # COOL
APP_THEME = "flatly.css"
# APP_THEME = "journal.css"
# APP_THEME = "readable.css"
# APP_THEME = "simplex.css"
# APP_THEME = "slate.css" # COOL
# APP_THEME = "spacelab.css" # NICE
# APP_THEME = "united.css"
# ----------------------------
FAB_ROLES = {
"ReadOnly": [
["DoctorModelView", "can_list"],
["DoctorModelView", "can_show"],
["back", "can_show"],
["DoctorModelView", "menu_access"],
["DoctorModelView", "can_get"],
["DoctorModelView", "can_info"],
["UserDBModelView", "can_list"],
["UserDBModelView", "can_show"],
["UserDBModelView", "menu_access"],
["UserDBModelView", "can_get"],
["UserDBModelView", "can_info"],
["UserDBModelView", "can_info"]
]
}
LOG_TO_STDOUT = os.environ.get('LOG_TO_STDOUT')
и мой init .py
import logging
import os
from config import Config
from flask_migrate import Migrate
from flask import Flask, request, current_app
from flask_appbuilder import AppBuilder, SQLA
from sqlalchemy import event
from sqlalchemy.engine import Engine
from app.index import MyIndexView
from logging.handlers import SMTPHandler, RotatingFileHandler
app = Flask(__name__)
app.config.from_object(Config)
db = SQLA(app)
appbuilder = AppBuilder(app, db.session, indexview=MyIndexView, base_template='mybase.html')
migrate = Migrate(app, db)
if not app.debug and not app.testing:
if app.config['LOG_TO_STDOUT']:
stream_handler = logging.StreamHandler()
stream_handler.setLevel(logging.INFO)
app.logger.addHandler(stream_handler)
else:
if not os.path.exists('logs'):
os.mkdir('logs')
file_handler = RotatingFileHandler('logs/microblog.log',
maxBytes=10240, backupCount=10)
file_handler.setFormatter(logging.Formatter(
'%(asctime)s %(levelname)s: %(message)s '
'[in %(pathname)s:%(lineno)d]'))
file_handler.setLevel(logging.INFO)
app.logger.addHandler(file_handler)
app.logger.setLevel(logging.INFO)
app.logger.info('Database startup')
logging.basicConfig(format="%(asctime)s:%(levelname)s:%(name)s:%(message)s")
logging.getLogger().setLevel(logging.DEBUG)
@event.listens_for(Engine, "connect")
def set_sqlite_pragma(dbapi_connection, connection_record):
cursor = dbapi_connection.cursor()
cursor.execute("PRAGMA foreign_keys=ON")
cursor.close()
from . import views, models # noqa
и мои требования.txt
alembic==1.4.1
apispec==1.3.3
attrs==19.3.0
Babel==2.8.0
Click==7.0
colorama==0.4.3
defusedxml==0.6.0
Flask==1.1.1
Flask-AppBuilder==2.2.4
Flask-Babel==1.0.0
Flask-JWT-Extended==3.24.1
Flask-Login==0.4.1
Flask-Migrate==2.5.2
Flask-OpenID==1.2.5
Flask-SQLAlchemy==2.4.1
Flask-WTF==0.14.3
gunicorn==20.0.4
importlib-metadata==1.5.0
itsdangerous==1.1.0
Jinja2==2.11.1
jsonschema==3.2.0
Mako==1.1.2
MarkupSafe==1.1.1
marshmallow==2.19.5
marshmallow-enum==1.5.1
marshmallow-sqlalchemy==0.22.3
Pillow==7.0.0
prison==0.1.2
psycopg2==2.8.4
PyJWT==1.7.1
pyrsistent==0.15.7
python-dateutil==2.8.1
python-dotenv==0.12.0
python-editor==1.0.4
python3-openid==3.1.0
pytz==2019.3
PyYAML==5.3
six==1.14.0
SQLAlchemy==1.3.13
SQLAlchemy-Utils==0.36.1
Werkzeug==1.0.0
WTForms==2.2.1
zipp==3.1.0
и мой Procfile для развертывания в Heroku
web: flask db upgrade; gunicorn app:app