python - 通过 Flask-SQL Alchemy 创建关联对象在 shell 中按预期工作,但在 Flask 路由中产生 JSON 错误
问题描述
我正在尝试创建一个关联对象,该对象定义和UserRelationship
之间的“跟随”关系。当我通过 shell 与这个对象交互时,它的行为与我预期的一样,但是当实现到路由函数时,它会产生一个“TypeError:UserRelationship 类型的对象不是 JSON 可序列化的”User
User
class UserRelationship(db.Model):
follows_id = db.Column(db.Integer, db.ForeignKey('user.id'), primary_key=True)
bank = db.Column(db.String(100))
follows = db.relationship('User', backref=db.backref('followers'))
class User(UserMixin, db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(64), index=True, unique=True)
follows = db.relationship('UserRelationship', backref=db.backref('followers', lazy=True))
这会在 shell 中产生预期的结果:
user1=User(username='joe')
user2=User(username='paul')
user3=User(username='phil')
new_follow = UserRelationship(follows=user2, bank='1000')
new_follow2 = UserRelationship(follows=user3, bank='1000')
user2.follows.append(new_follow)
user2.follows.append(new_follow2)
db.session.commit()
user2.follows
>>[<UserRelationship (transient 2627038264968)>, <UserRelationship (transient 2627038286344)>]
user2.follows[0].bank
'1000'
user2.follows[0].follows
<User Paul>
但是,当我在我的 Flask 应用程序上实际使用它时,它会运行一个错误:
`TypeError:UserRelationship 类型的对象不是 JSON 可序列化的'
这是我尝试在实际应用程序上实现的方式:
@app.route('/follow/<to_follow>', methods=['GET', 'POST'])
@login_required
def follow(to_follow):
follow_target = User.query.filter_by(username=to_follow).first()
form = FollowForm()
if form.validate_on_submit():
new_follow = UserRelationship(bank='1000', follows=follow_target)
current_user.follows.append(new_follow)
db.session.commit()
flash(current_user.follows)
return redirect(profile)
return render_template('follow.html',
title=f'Follow {to_follow}',
form=form,
to_follow=follow_target)
完整追溯:
Traceback (most recent call last):
File "c:\users\phill\anaconda3\envs\zigenv\lib\site-packages\flask\app.py", line 2446, in wsgi_app
response = self.full_dispatch_request()
File "c:\users\phill\anaconda3\envs\zigenv\lib\site-packages\flask\app.py", line 1952, in full_dispatch_request
return self.finalize_request(rv)
File "c:\users\phill\anaconda3\envs\zigenv\lib\site-packages\flask\app.py", line 1969, in finalize_request
response = self.process_response(response)
File "c:\users\phill\anaconda3\envs\zigenv\lib\site-packages\flask\app.py", line 2268, in process_response
self.session_interface.save_session(self, ctx.session, response)
File "c:\users\phill\anaconda3\envs\zigenv\lib\site-packages\flask\sessions.py", line 378, in save_session
val = self.get_signing_serializer(app).dumps(dict(session))
File "c:\users\phill\anaconda3\envs\zigenv\lib\site-packages\itsdangerous\serializer.py", line 166, in dumps
payload = want_bytes(self.dump_payload(obj))
File "c:\users\phill\anaconda3\envs\zigenv\lib\site-packages\itsdangerous\url_safe.py", line 42, in dump_payload
json = super(URLSafeSerializerMixin, self).dump_payload(obj)
File "c:\users\phill\anaconda3\envs\zigenv\lib\site-packages\itsdangerous\serializer.py", line 133, in dump_payload
return want_bytes(self.serializer.dumps(obj, **self.serializer_kwargs))
File "c:\users\phill\anaconda3\envs\zigenv\lib\site-packages\flask\json\tag.py", line 305, in dumps
return dumps(self.tag(value), separators=(",", ":"))
File "c:\users\phill\anaconda3\envs\zigenv\lib\site-packages\flask\json\__init__.py", line 211, in dumps
rv = _json.dumps(obj, **kwargs)
File "c:\users\phill\anaconda3\envs\zigenv\lib\json\__init__.py", line 238, in dumps
**kw).encode(obj)
File "c:\users\phill\anaconda3\envs\zigenv\lib\json\encoder.py", line 199, in encode
chunks = self.iterencode(o, _one_shot=True)
File "c:\users\phill\anaconda3\envs\zigenv\lib\json\encoder.py", line 257, in iterencode
return _iterencode(o, 0)
File "c:\users\phill\anaconda3\envs\zigenv\lib\site-packages\flask\json\__init__.py", line 100, in default
return _json.JSONEncoder.default(self, o)
File "c:\users\phill\anaconda3\envs\zigenv\lib\json\encoder.py", line 179, in default
raise TypeError(f'Object of type {o.__class__.__name__} '
TypeError: Object of type UserRelationship is not JSON serializable
[2019-09-29 16:10:38,924] ERROR in app: Request finalizing failed with an error while handling an error
Traceback (most recent call last):
File "c:\users\phill\anaconda3\envs\zigenv\lib\site-packages\flask\app.py", line 2446, in wsgi_app
response = self.full_dispatch_request()
File "c:\users\phill\anaconda3\envs\zigenv\lib\site-packages\flask\app.py", line 1952, in full_dispatch_request
return self.finalize_request(rv)
File "c:\users\phill\anaconda3\envs\zigenv\lib\site-packages\flask\app.py", line 1969, in finalize_request
response = self.process_response(response)
File "c:\users\phill\anaconda3\envs\zigenv\lib\site-packages\flask\app.py", line 2268, in process_response
self.session_interface.save_session(self, ctx.session, response)
File "c:\users\phill\anaconda3\envs\zigenv\lib\site-packages\flask\sessions.py", line 378, in save_session
val = self.get_signing_serializer(app).dumps(dict(session))
File "c:\users\phill\anaconda3\envs\zigenv\lib\site-packages\itsdangerous\serializer.py", line 166, in dumps
payload = want_bytes(self.dump_payload(obj))
File "c:\users\phill\anaconda3\envs\zigenv\lib\site-packages\itsdangerous\url_safe.py", line 42, in dump_payload
json = super(URLSafeSerializerMixin, self).dump_payload(obj)
File "c:\users\phill\anaconda3\envs\zigenv\lib\site-packages\itsdangerous\serializer.py", line 133, in dump_payload
return want_bytes(self.serializer.dumps(obj, **self.serializer_kwargs))
File "c:\users\phill\anaconda3\envs\zigenv\lib\site-packages\flask\json\tag.py", line 305, in dumps
return dumps(self.tag(value), separators=(",", ":"))
File "c:\users\phill\anaconda3\envs\zigenv\lib\site-packages\flask\json\__init__.py", line 211, in dumps
rv = _json.dumps(obj, **kwargs)
File "c:\users\phill\anaconda3\envs\zigenv\lib\json\__init__.py", line 238, in dumps
**kw).encode(obj)
File "c:\users\phill\anaconda3\envs\zigenv\lib\json\encoder.py", line 199, in encode
chunks = self.iterencode(o, _one_shot=True)
File "c:\users\phill\anaconda3\envs\zigenv\lib\json\encoder.py", line 257, in iterencode
return _iterencode(o, 0)
File "c:\users\phill\anaconda3\envs\zigenv\lib\site-packages\flask\json\__init__.py", line 100, in default
return _json.JSONEncoder.default(self, o)
File "c:\users\phill\anaconda3\envs\zigenv\lib\json\encoder.py", line 179, in default
raise TypeError(f'Object of type {o.__class__.__name__} '
TypeError: Object of type UserRelationship is not JSON serializable
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "c:\users\phill\anaconda3\envs\zigenv\lib\site-packages\flask\app.py", line 1969, in finalize_request
response = self.process_response(response)
File "c:\users\phill\anaconda3\envs\zigenv\lib\site-packages\flask\app.py", line 2268, in process_response
self.session_interface.save_session(self, ctx.session, response)
File "c:\users\phill\anaconda3\envs\zigenv\lib\site-packages\flask\sessions.py", line 378, in save_session
val = self.get_signing_serializer(app).dumps(dict(session))
File "c:\users\phill\anaconda3\envs\zigenv\lib\site-packages\itsdangerous\serializer.py", line 166, in dumps
payload = want_bytes(self.dump_payload(obj))
File "c:\users\phill\anaconda3\envs\zigenv\lib\site-packages\itsdangerous\url_safe.py", line 42, in dump_payload
json = super(URLSafeSerializerMixin, self).dump_payload(obj)
File "c:\users\phill\anaconda3\envs\zigenv\lib\site-packages\itsdangerous\serializer.py", line 133, in dump_payload
return want_bytes(self.serializer.dumps(obj, **self.serializer_kwargs))
File "c:\users\phill\anaconda3\envs\zigenv\lib\site-packages\flask\json\tag.py", line 305, in dumps
return dumps(self.tag(value), separators=(",", ":"))
File "c:\users\phill\anaconda3\envs\zigenv\lib\site-packages\flask\json\__init__.py", line 211, in dumps
rv = _json.dumps(obj, **kwargs)
File "c:\users\phill\anaconda3\envs\zigenv\lib\json\__init__.py", line 238, in dumps
**kw).encode(obj)
File "c:\users\phill\anaconda3\envs\zigenv\lib\json\encoder.py", line 199, in encode
chunks = self.iterencode(o, _one_shot=True)
File "c:\users\phill\anaconda3\envs\zigenv\lib\json\encoder.py", line 257, in iterencode
return _iterencode(o, 0)
File "c:\users\phill\anaconda3\envs\zigenv\lib\site-packages\flask\json\__init__.py", line 100, in default
return _json.JSONEncoder.default(self, o)
File "c:\users\phill\anaconda3\envs\zigenv\lib\json\encoder.py", line 179, in default
raise TypeError(f'Object of type {o.__class__.__name__} '
TypeError: Object of type UserRelationship is not JSON serializable
解决方案
实际上,db.Model
该类没有提供以 JSON 格式序列化其实例的方法。例如,从Flask-SqlAlchemy 快速入门中提取的以下代码由于相同的原因而失败。
import json
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db'
db = SQLAlchemy(app)
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True, nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
if __name__ == '__main__':
db.create_all()
admin = User(username='admin', email='admin@example.com')
json.dumps(admin)
你得到:
Traceback (most recent call last):
[...]
TypeError: Object of type User is not JSON serializable
因此,您UserRelationship
继承的类db.Model
不是 JSON 可序列化的。
当服务器想要保存当前会话中的用户状态时,就会出现问题。它转储User
实例,然后转储它的UserRelationship
.
必须有一种方法可以避免将关系存储在会话中。但这需要更多的调查。
编辑
IMO,UserRelationship
应该是a和它自身之间的关联对象。User
这是一种带有额外列的自引用多对多关系。
基于这个this answer,你应该这样定义它:
class UserRelationship(db.Model):
fk_user_from = db.Column(db.Integer, db.ForeignKey("user.id"), primary_key=True)
fk_user_to = db.Column(db.Integer, db.ForeignKey("user.id"), primary_key=True)
bank = db.Column(db.String(100))
class User(UserMixin, db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True, nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
followings = db.relationship(
"UserRelationship", backref="followed", primaryjoin=id == UserRelationship.fk_user_to
)
followers = db.relationship(
"UserRelationship", backref="following", primaryjoin=id == UserRelationship.fk_user_from
)
你能用这个模式重试吗?
推荐阅读
- docker - 如何允许从本地主机和容器到 ASP.NET Core Web API 应用程序的 HTTPS 连接?
- r - 如何删除具有空值的列/行?
- c# - 从 SQL 数据库(来自不同的表)获取 2 个或更多不同的信息
- html - 如何在带有列表项的块子元素的html / css有序列表中使项目编号具有自适应性?
- javascript - 从不同的组件打开模态
- c - PsSuspendProcess 阻塞/等待/卡住 - Windows 内核编程
- android - android studio中当前检查配置文件有什么用?
- r - 将字符串传递给 R 函数并将其用作函数中的列名
- android - 原生广告未出现在 Android 应用的发布版本中
- python - AssertionError:无法计算输出张量