背景
我们正在使用Flask来开发一个相当简单的应用程序来解决问题"我们意识到我们在以前的项目上工作过。会话管理通过Flask-Login扩展程序以及我们用于用户身份验证的Github-Flask扩展程序进行处理。该应用程序使用Flask-SQLAlchemy和Psycopg2(2.6.3)连接到存储用户数据的PostgreSQL 9.1数据库。
问题
当应用程序在本地运行(或部署到远程服务器)进行测试时,我们在第一次登录尝试时看到了一个奇怪的情况。我第一次登录时成功通过Github身份验证集,但立即看到此错误。
DataError: (DataError) invalid input syntax for integer: "None"
LINE 3: WHERE "user".id = 'None'
^
'SELECT "user".id AS user_id, "user".date_created AS user_date_created,
"user".date_modified AS user_date_modified, "user".nickname AS user_nickname,
"user".email AS user_email, "user".about_me AS user_about_me,
"user".github_access_token AS user_github_access_token, "user".github_id AS
user_github_id \nFROM "user" \nWHERE "user".id = %(param_1)s' {'param_1': u'None'}
我可以关闭浏览器标签,然后重新访问应用程序网址并查看相同的消息。但是,如果我关闭浏览器窗口,完全清除浏览器的缓存,然后尝试重新登录,它会按预期工作,并且我可以使用该应用程序。从那时起,我可以退出,切换浏览器,再次清除浏览器缓存等等,并且似乎没有任何问题 - 它只是首次登录,直到清除浏览器缓存发生的情况。
我的测试显示,这种情况发生在任何用户身上,两个不同的用户可能同时处于两种不同的状态(一个已清除缓存,现在没有问题,另一个没有采取过这些步骤仍然处于错误状态。
最初我们使用的是本地SQLite数据库而不是Postgres。这个问题不存在。它只发生在切换到Postgres之后。
我不知道我可采取哪些步骤来纠正这种情况。到目前为止,我所知道的问题在网上搜索时无法找到正确的答案。
作为参考,我还将包括完整的堆栈跟踪。
完整堆栈跟踪
Traceback (most recent call last):
File "/Users/dev/PRODUCT/venv/lib/python2.7/site-packages/flask/app.py", line 1836, in __call__
return self.wsgi_app(environ, start_response)
File "/Users/dev/PRODUCT/venv/lib/python2.7/site-packages/flask/app.py", line 1820, in wsgi_app
response = self.make_response(self.handle_exception(e))
File "/Users/dev/PRODUCT/venv/lib/python2.7/site-packages/flask/app.py", line 1403, in handle_exception
reraise(exc_type, exc_value, tb)
File "/Users/dev/PRODUCT/venv/lib/python2.7/site-packages/flask/app.py", line 1817, in wsgi_app
response = self.full_dispatch_request()
File "/Users/dev/PRODUCT/venv/lib/python2.7/site-packages/flask/app.py", line 1477, in full_dispatch_request
rv = self.handle_user_exception(e)
File "/Users/dev/PRODUCT/venv/lib/python2.7/site-packages/flask/app.py", line 1381, in handle_user_exception
reraise(exc_type, exc_value, tb)
File "/Users/dev/PRODUCT/venv/lib/python2.7/site-packages/flask/app.py", line 1475, in full_dispatch_request
rv = self.dispatch_request()
File "/Users/dev/PRODUCT/venv/lib/python2.7/site-packages/flask_debugtoolbar/__init__.py", line 124, in dispatch_request
return view_func(**req.view_args)
File "/Users/dev/PRODUCT/app/modules/mod_profile/controllers.py", line 26, in profile
title='PRODUCT')
File "/Users/dev/PRODUCT/venv/lib/python2.7/site-packages/flask/templating.py", line 126, in render_template
ctx.app.update_template_context(context)
File "/Users/dev/PRODUCT/venv/lib/python2.7/site-packages/flask/app.py", line 716, in update_template_context
context.update(func())
File "/Users/dev/PRODUCT/venv/lib/python2.7/site-packages/flask_login.py", line 825, in _user_context_processor
return dict(current_user=_get_user())
File "/Users/dev/PRODUCT/venv/lib/python2.7/site-packages/flask_login.py", line 794, in _get_user
current_app.login_manager._load_user()
File "/Users/dev/PRODUCT/venv/lib/python2.7/site-packages/flask_login.py", line 363, in _load_user
return self.reload_user()
File "/Users/dev/PRODUCT/venv/lib/python2.7/site-packages/flask_login.py", line 325, in reload_user
user = self.user_callback(user_id)
File "/Users/dev/PRODUCT/app/modules/mod_auth/controllers.py", line 31, in load_user
return User.query.get(id)
File "/Users/dev/PRODUCT/venv/lib/python2.7/site-packages/sqlalchemy/orm/query.py", line 840, in get
return loading.load_on_ident(self, key)
File "/Users/dev/PRODUCT/venv/lib/python2.7/site-packages/sqlalchemy/orm/loading.py", line 231, in load_on_ident
return q.one()
File "/Users/dev/PRODUCT/venv/lib/python2.7/site-packages/sqlalchemy/orm/query.py", line 2395, in one
ret = list(self)
File "/Users/dev/PRODUCT/venv/lib/python2.7/site-packages/sqlalchemy/orm/query.py", line 2438, in __iter__
return self._execute_and_instances(context)
File "/Users/dev/PRODUCT/venv/lib/python2.7/site-packages/sqlalchemy/orm/query.py", line 2453, in _execute_and_instances
result = conn.execute(querycontext.statement, self._params)
File "/Users/dev/PRODUCT/venv/lib/python2.7/site-packages/sqlalchemy/engine/base.py", line 729, in execute
return meth(self, multiparams, params)
File "/Users/dev/PRODUCT/venv/lib/python2.7/site-packages/sqlalchemy/sql/elements.py", line 322, in _execute_on_connection
return connection._execute_clauseelement(self, multiparams, params)
File "/Users/dev/PRODUCT/venv/lib/python2.7/site-packages/sqlalchemy/engine/base.py", line 826, in _execute_clauseelement
compiled_sql, distilled_params
File "/Users/dev/PRODUCT/venv/lib/python2.7/site-packages/sqlalchemy/engine/base.py", line 958, in _execute_context
context)
File "/Users/dev/PRODUCT/venv/lib/python2.7/site-packages/sqlalchemy/engine/base.py", line 1159, in _handle_dbapi_exception
exc_info
File "/Users/dev/PRODUCT/venv/lib/python2.7/site-packages/sqlalchemy/util/compat.py", line 199, in raise_from_cause
reraise(type(exception), exception, tb=exc_tb)
File "/Users/dev/PRODUCT/venv/lib/python2.7/site-packages/sqlalchemy/engine/base.py", line 951, in _execute_context
context)
File "/Users/dev/PRODUCT/venv/lib/python2.7/site-packages/sqlalchemy/engine/default.py", line 436, in do_execute
cursor.execute(statement, parameters)
DataError: (DataError) invalid input syntax for integer: "None"
LINE 3: WHERE "user".id = 'None'
^
'SELECT "user".id AS user_id, "user".date_created AS user_date_created, "user".date_modified AS user_date_modified, "user".nickname AS user_nickname, "user".email AS user_email, "user".about_me AS user_about_me, "user".github_access_token AS user_github_access_token, "user".github_id AS user_github_id \nFROM "user" \nWHERE "user".id = %(param_1)s' {'param_1': u'None'}
答案 0 :(得分:1)
解决!
这是一场竞赛。在登录过程中,我们的应用程序将经过身份验证的用户重定向到其个人资料页面。这被正确触发,但对于新用户,它在用户实际提交到数据库之前被调用。
视觉上它同时发生,所以你可以确认回调是否提供了“正确”的信息,但直到我解构然后重建登录步骤才意识到它正在击中路线就在记录实际提交到数据库之前。由于添加记录的功能没有被重定向中断,它完成了应该做的事情,但只是迟到了。
在对类似问题进行排查时,也许其他人会发现我的代码摘录很有用:
## from mod_auth/controllers.py
@mod_auth.before_app_request
def before_request():
g.user = current_user
@mod_auth.route('/login', methods=['GET'])
def login():
if g.user.is_authenticated():
redirect(url_for('mod_home.index'))
return github.authorize()
@login_manager.user_loader
def load_user(id):
return User.query.get(id)
@github.access_token_getter
def token_getter():
user = g.user
if user is not None:
return user.github_access_token
@mod_auth.route('/github')
@github.authorized_handler
def authorized(oauth_token):
next_url = request.args.get('next') or url_for('mod_home.index')
if oauth_token is None:
flash("Authorization failed.")
return redirect(next_url)
user = User.query.filter_by(github_access_token=oauth_token).first()
if user is None:
user = User(github_access_token=oauth_token)
db.session.add(user)
db.session.commit() ## <-- adding a commit here fixed the issue
user.github_access_token = oauth_token
login_user(user)
## login_user called, but because the oauth_token exist and had been
## added to the session, the user was flagged as "is_authenticated"
## and the redirect was triggered
ghinfo = github.get('user')
if 'login' in ghinfo:
user.nickname = ghinfo['login'].lower()
else:
user.nickname = 'nameless'
db.session.commit() ## this was the only commit, originally
return redirect(url_for('mod_profile.profile', username=user.nickname))