问题
I'm trying to create an association object UserRelationship which defines a 'follow' relationship between User and User. When I interface with this object through shell, it behaves as I expect, but when implemented onto a route function it produces a `TypeError: Object of type UserRelationship is not JSON serializable'
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))
This produces expected results in the 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>
However, when I have this actually on my Flask app, it runs an error:
`TypeError: Object of type UserRelationship is not JSON serializable'
Here's how I'm trying to implement on the actual application:
@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)
Full traceback:
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
回答1:
Actually, the db.Model class doesn't provides a method to serialize its instance in JSON format. For, instance, the following code, extracted from the Flask-SqlAlchemy quick start fails for the same reason.
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)
You get:
Traceback (most recent call last):
[...]
TypeError: Object of type User is not JSON serializable
So, your UserRelationship class, which inherits db.Model, is not JSON serializable.
The problem occurs when the server wants to save the user state in the current session. It dumps the User instance and then its UserRelationship.
There must be a way to avoid storing the relationships in the session. But that needs more investigation.
EDIT
IMO, the UserRelationship should be a Association Object between a User and it-self. It's a kind of self referential Many-to-many relationship with extra column.
Based on this this answer, you should define it like that:
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
)
Could you retry with this schema?
来源:https://stackoverflow.com/questions/58154463/creating-association-object-through-flask-sql-alchemy-works-as-expected-in-shell