通过Flask-SQL Alchemy创建关联对象可以在Shell中按预期工作,但在Flask路由中会产生JSON错误 [英] Creating association object through Flask-SQL Alchemy works as expected in shell, but produces JSON error in Flask route

查看:102
本文介绍了通过Flask-SQL Alchemy创建关联对象可以在Shell中按预期工作,但在Flask路由中会产生JSON错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试创建关联对象UserRelationship,该对象定义了UserUser之间的跟随"关系.当我通过外壳与该对象进行交互时,它的行为符合我的预期,但是当实现到路由函数上时,它会产生"TypeError:UserRelationship类型的对象不可JSON序列化"

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))

这会在shell中产生预期的结果:

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>

但是,当我在Flask应用程序上实际使用它时,它会运行错误:

However, when I have this actually on my Flask app, it runs an error:

`TypeError:UserRelationship类型的对象不可JSON序列化'

`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)

完整追溯:

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快速入门由于相同的原因而失败.

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)

您得到:

Traceback (most recent call last):
  [...]
TypeError: Object of type User is not JSON serializable

因此,您的UserRelationship类继承了db.Model,因此无法JSON序列化.

So, your UserRelationship class, which inherits db.Model, is not JSON serializable.

当服务器希望在当前会话中保存用户状态时,会出现问题.它先转储User实例,然后转储其UserRelationship.

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.

编辑

IMO,UserRelationship应该是关联对象User和它本身之间.这是一种自引用的多对多关系,带有额外的列.

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
    )

您可以重试此架构吗?

这篇关于通过Flask-SQL Alchemy创建关联对象可以在Shell中按预期工作,但在Flask路由中会产生JSON错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆