因此,我使用 Flask-Marshmallows 转储数据库模型并将它们转换为 Python 对象以适合我的 Web API。
但是,由于某种原因,程序总是会出现以下错误:
Traceback (most recent call at last):
...
File "/Volumes/Data/Develop/Python/Orizont/venv/lib/python3.8/site-packages/marshmallow/fields.py", line 624, in _serialize
return schema.dump(nested_obj, many=many)
File "/Volumes/Data/Develop/Python/Orizont/venv/lib/python3.8/site-packages/marshmallow/schema.py", line 552, in dump
result = self._serialize(processed_obj, many=many)
File "/Volumes/Data/Develop/Python/Orizont/venv/lib/python3.8/site-packages/marshmallow/schema.py", line 520, in _serialize
value = field_obj.serialize(attr_name, obj, accessor=self.get_attribute)
File "/Volumes/Data/Develop/Python/Orizont/venv/lib/python3.8/site-packages/marshmallow/fields.py", line 338, in serialize
return self._serialize(value, attr, obj, **kwargs)
File "/Volumes/Data/Develop/Python/Orizont/venv/lib/python3.8/site-packages/marshmallow/fields.py", line 624, in _serialize
return schema.dump(nested_obj, many=many)
File "/Volumes/Data/Develop/Python/Orizont/venv/lib/python3.8/site-packages/marshmallow/schema.py", line 552, in dump
result = self._serialize(processed_obj, many=many)
File "/Volumes/Data/Develop/Python/Orizont/venv/lib/python3.8/site-packages/marshmallow/schema.py", line 520, in _serialize
value = field_obj.serialize(attr_name, obj, accessor=self.get_attribute)
File "/Volumes/Data/Develop/Python/Orizont/venv/lib/python3.8/site-packages/marshmallow/fields.py", line 338, in serialize
return self._serialize(value, attr, obj, **kwargs)
File "/Volumes/Data/Develop/Python/Orizont/venv/lib/python3.8/site-packages/marshmallow/fields.py", line 624, in _serialize
return schema.dump(nested_obj, many=many)
File "/Volumes/Data/Develop/Python/Orizont/venv/lib/python3.8/site-packages/marshmallow/schema.py", line 552, in dump
result = self._serialize(processed_obj, many=many)
File "/Volumes/Data/Develop/Python/Orizont/venv/lib/python3.8/site-packages/marshmallow/schema.py", line 520, in _serialize
value = field_obj.serialize(attr_name, obj, accessor=self.get_attribute)
File "/Volumes/Data/Develop/Python/Orizont/venv/lib/python3.8/site-packages/marshmallow/fields.py", line 338, in serialize
return self._serialize(value, attr, obj, **kwargs)
File "/Volumes/Data/Develop/Python/Orizont/venv/lib/python3.8/site-packages/marshmallow/fields.py", line 757, in _serialize
return [self.inner._serialize(each, attr, obj, **kwargs) for each in value]
File "/Volumes/Data/Develop/Python/Orizont/venv/lib/python3.8/site-packages/marshmallow/fields.py", line 757, in <listcomp>
return [self.inner._serialize(each, attr, obj, **kwargs) for each in value]
File "/Volumes/Data/Develop/Python/Orizont/venv/lib/python3.8/site-packages/marshmallow/fields.py", line 624, in _serialize
return schema.dump(nested_obj, many=many)
File "/Volumes/Data/Develop/Python/Orizont/venv/lib/python3.8/site-packages/marshmallow/schema.py", line 552, in dump
result = self._serialize(processed_obj, many=many)
File "/Volumes/Data/Develop/Python/Orizont/venv/lib/python3.8/site-packages/marshmallow/schema.py", line 520, in _serialize
value = field_obj.serialize(attr_name, obj, accessor=self.get_attribute)
File "/Volumes/Data/Develop/Python/Orizont/venv/lib/python3.8/site-packages/marshmallow/fields.py", line 338, in serialize
return self._serialize(value, attr, obj, **kwargs)
File "/Volumes/Data/Develop/Python/Orizont/venv/lib/python3.8/site-packages/marshmallow/fields.py", line 624, in _serialize
return schema.dump(nested_obj, many=many)
File "/Volumes/Data/Develop/Python/Orizont/venv/lib/python3.8/site-packages/marshmallow/schema.py", line 552, in dump
result = self._serialize(processed_obj, many=many)
File "/Volumes/Data/Develop/Python/Orizont/venv/lib/python3.8/site-packages/marshmallow/schema.py", line 520, in _serialize
value = field_obj.serialize(attr_name, obj, accessor=self.get_attribute)
File "/Volumes/Data/Develop/Python/Orizont/venv/lib/python3.8/site-packages/marshmallow/fields.py", line 338, in serialize
return self._serialize(value, attr, obj, **kwargs)
File "/Volumes/Data/Develop/Python/Orizont/venv/lib/python3.8/site-packages/marshmallow/fields.py", line 624, in _serialize
return schema.dump(nested_obj, many=many)
File "/Volumes/Data/Develop/Python/Orizont/venv/lib/python3.8/site-packages/marshmallow/schema.py", line 552, in dump
result = self._serialize(processed_obj, many=many)
File "/Volumes/Data/Develop/Python/Orizont/venv/lib/python3.8/site-packages/marshmallow/schema.py", line 520, in _serialize
value = field_obj.serialize(attr_name, obj, accessor=self.get_attribute)
File "/Volumes/Data/Develop/Python/Orizont/venv/lib/python3.8/site-packages/marshmallow/fields.py", line 338, in serialize
return self._serialize(value, attr, obj, **kwargs)
File "/Volumes/Data/Develop/Python/Orizont/venv/lib/python3.8/site-packages/marshmallow/fields.py", line 757, in _serialize
return [self.inner._serialize(each, attr, obj, **kwargs) for each in value]
File "/Volumes/Data/Develop/Python/Orizont/venv/lib/python3.8/site-packages/marshmallow/fields.py", line 757, in <listcomp>
return [self.inner._serialize(each, attr, obj, **kwargs) for each in value]
File "/Volumes/Data/Develop/Python/Orizont/venv/lib/python3.8/site-packages/marshmallow/fields.py", line 624, in _serialize
return schema.dump(nested_obj, many=many)
File "/Volumes/Data/Develop/Python/Orizont/venv/lib/python3.8/site-packages/marshmallow/schema.py", line 552, in dump
result = self._serialize(processed_obj, many=many)
File "/Volumes/Data/Develop/Python/Orizont/venv/lib/python3.8/site-packages/marshmallow/schema.py", line 520, in _serialize
value = field_obj.serialize(attr_name, obj, accessor=self.get_attribute)
File "/Volumes/Data/Develop/Python/Orizont/venv/lib/python3.8/site-packages/marshmallow/fields.py", line 338, in serialize
return self._serialize(value, attr, obj, **kwargs)
File "/Volumes/Data/Develop/Python/Orizont/venv/lib/python3.8/site-packages/marshmallow/fields.py", line 624, in _serialize
return schema.dump(nested_obj, many=many)
File "/Volumes/Data/Develop/Python/Orizont/venv/lib/python3.8/site-packages/marshmallow/schema.py", line 552, in dump
result = self._serialize(processed_obj, many=many)
File "/Volumes/Data/Develop/Python/Orizont/venv/lib/python3.8/site-packages/marshmallow/schema.py", line 520, in _serialize
value = field_obj.serialize(attr_name, obj, accessor=self.get_attribute)
File "/Volumes/Data/Develop/Python/Orizont/venv/lib/python3.8/site-packages/marshmallow/fields.py", line 338, in serialize
return self._serialize(value, attr, obj, **kwargs)
File "/Volumes/Data/Develop/Python/Orizont/venv/lib/python3.8/site-packages/marshmallow/fields.py", line 624, in _serialize
return schema.dump(nested_obj, many=many)
File "/Volumes/Data/Develop/Python/Orizont/venv/lib/python3.8/site-packages/marshmallow/schema.py", line 552, in dump
result = self._serialize(processed_obj, many=many)
File "/Volumes/Data/Develop/Python/Orizont/venv/lib/python3.8/site-packages/marshmallow/schema.py", line 520, in _serialize
value = field_obj.serialize(attr_name, obj, accessor=self.get_attribute)
File "/Volumes/Data/Develop/Python/Orizont/venv/lib/python3.8/site-packages/marshmallow/fields.py", line 330, in serialize
value = self.get_value(obj, attr, accessor=accessor)
File "/Volumes/Data/Develop/Python/Orizont/venv/lib/python3.8/site-packages/marshmallow/fields.py", line 259, in get_value
return accessor_func(obj, check_key, default)
File "/Volumes/Data/Develop/Python/Orizont/venv/lib/python3.8/site-packages/marshmallow/schema.py", line 477, in get_attribute
return get_value(obj, attr, default)
File "/Volumes/Data/Develop/Python/Orizont/venv/lib/python3.8/site-packages/marshmallow/utils.py", line 239, in get_value
return _get_value_for_key(obj, key, default)
File "/Volumes/Data/Develop/Python/Orizont/venv/lib/python3.8/site-packages/marshmallow/utils.py", line 253, in _get_value_for_key
return getattr(obj, key, default)
File "/Volumes/Data/Develop/Python/Orizont/venv/lib/python3.8/site-packages/sqlalchemy/orm/attributes.py", line 481, in __get__
return self.impl.get(state, dict_)
File "/Volumes/Data/Develop/Python/Orizont/venv/lib/python3.8/site-packages/sqlalchemy/orm/dynamic.py", line 101, in get
return self.query_class(self, state)
File "/Volumes/Data/Develop/Python/Orizont/venv/lib/python3.8/site-packages/sqlalchemy/orm/dynamic.py", line 308, in __init__
prop._with_parent(instance, alias_secondary=False),
File "/Volumes/Data/Develop/Python/Orizont/venv/lib/python3.8/site-packages/sqlalchemy/orm/relationships.py", line 1674, in _with_parent
return self._optimized_compare(
File "/Volumes/Data/Develop/Python/Orizont/venv/lib/python3.8/site-packages/sqlalchemy/orm/relationships.py", line 1741, in _optimized_compare
criterion = visitors.cloned_traverse(
File "/Volumes/Data/Develop/Python/Orizont/venv/lib/python3.8/site-packages/sqlalchemy/sql/visitors.py", line 783, in cloned_traverse
obj = clone(
File "/Volumes/Data/Develop/Python/Orizont/venv/lib/python3.8/site-packages/sqlalchemy/sql/visitors.py", line 776, in clone
newelem._copy_internals(clone=clone, **kw)
File "/Volumes/Data/Develop/Python/Orizont/venv/lib/python3.8/site-packages/sqlalchemy/sql/traversals.py", line 745, in _copy_internals
result = meth(attrname, self, obj, **kw)
File "/Volumes/Data/Develop/Python/Orizont/venv/lib/python3.8/site-packages/sqlalchemy/sql/traversals.py", line 757, in visit_clauseelement
return clone(element, **kw)
File "/Volumes/Data/Develop/Python/Orizont/venv/lib/python3.8/site-packages/sqlalchemy/sql/visitors.py", line 775, in clone
cloned[id(elem)] = newelem = elem._clone(**kw)
File "/Volumes/Data/Develop/Python/Orizont/venv/lib/python3.8/site-packages/sqlalchemy/sql/elements.py", line 1622, in _clone
c.key = _anonymous_label.safe_construct(
File "/Volumes/Data/Develop/Python/Orizont/venv/lib/python3.8/site-packages/sqlalchemy/sql/elements.py", line 5230, in safe_construct
body = re.sub(r"[%\(\) \$]+", "_", body).strip("_")
File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/re.py", line 210, in sub
return _compile(pattern, flags).sub(repl, string, count)
File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/re.py", line 291, in _compile
if isinstance(flags, RegexFlag):
RecursionError: maximum recursion depth exceeded while calling a Python object
我没有看到任何可能无限递归的字段...有什么想法吗?谢谢!
我的架构文件:
from flask_sqlalchemy import model
from .extensions import ma
from .models import User, Discussion, Tag, Comment, Reply
class UserSchema(ma.SQLAlchemySchema):
class Meta:
model = User
id = ma.auto_field()
username = ma.auto_field()
email = ma.auto_field()
bio = ma.auto_field()
desc = ma.auto_field()
avatar = ma.auto_field()
discussions = ma.List(ma.Nested(lambda: DiscussionSchema(exclude=("author",))))
comments = ma.List(ma.Nested(lambda: CommentSchema(exclude=("author",))))
replies = ma.List(ma.Nested(lambda: ReplySchema(exclude=("author",))))
class DiscussionSchema(ma.SQLAlchemySchema):
class Meta:
model = Discussion
id = ma.auto_field()
title = ma.auto_field()
content = ma.auto_field()
author_id = ma.auto_field()
author = ma.Nested(UserSchema, exclude=("discussions",))
comments = ma.List(ma.Nested(lambda: CommentSchema(exclude=("discussion",))))
replies = ma.List(ma.Nested(lambda: ReplySchema(exclude=("discussion",))))
class TagSchema(ma.SQLAlchemySchema):
class Meta:
model = Tag
id = ma.auto_field()
name = ma.auto_field()
class ReplySchema(ma.SQLAlchemySchema):
class Meta:
model = Reply
id = ma.auto_field()
content = ma.auto_field()
author = ma.Nested(UserSchema, exclude=("replies",))
author_id = ma.auto_field()
discussion = ma.Nested(DiscussionSchema, exclude=("replies",))
discussion_id = ma.auto_field()
comments = ma.List(ma.Nested(lambda: CommentSchema(exclude=("reply",))))
class CommentSchema(ma.SQLAlchemySchema):
class Meta:
model = Comment
id = ma.auto_field()
content = ma.auto_field()
to_discussion = ma.auto_field()
author = ma.Nested(UserSchema, exclude=("comments",))
author_id = ma.auto_field()
discussion = ma.Nested(DiscussionSchema, exclude=("comments",))
discussion_id = ma.auto_field()
reply = ma.Nested(lambda: ReplySchema(exclude=("comments",)))
reply_id = ma.auto_field()
还有我的模特:
from .extensions import db
from bcrypt import hashpw as hash_password, checkpw as check_password, gensalt as random_salt
from itsdangerous import (TimedJSONWebSignatureSerializer as Serializer, BadSignature, SignatureExpired)
from flask import current_app as app
from flask_login import UserMixin
from libgravatar import Gravatar
class User(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String, unique=True, nullable=False)
email = db.Column(db.String, unique=True, nullable=False)
password_hash = db.Column(db.String, nullable=False)
desc = db.Column(db.String)
bio = db.Column(db.String)
avatar = db.Column(db.String)
discussions = db.relationship('Discussion', backref='author', lazy='dynamic')
comments = db.relationship('Comment', backref='author', lazy='dynamic')
replies = db.relationship('Reply', backref='author', lazy='dynamic')
@property
def password(self):
return "Password not readable."
@password.setter
def password(self, value):
self.password_hash = hash_password(value.encode('utf-8'), random_salt())
def generate_avatar(self):
gravatar = Gravatar(self.email)
self.avatar = gravatar.get_image(default="retro")
def verify_password(self, password):
return check_password(password.encode("utf-8"), self.password_hash)
def generate_auth_token(self, expiration=600):
s = Serializer(app.config["SECRET_KEY"], expires_in=expiration)
return s.dumps({"id": self.id})
@staticmethod
def verify_auth_token(token):
s = Serializer(app.config["SECRET_KEY"])
try:
data = s.loads(token)
except SignatureExpired:
return False
except BadSignature:
return False
user = User.query.get(data["id"])
return user
def is_active(self):
return False
def __repr__(self):
return '<User %s>' % self.username
tags = db.Table("tags",
db.Column("tag_id", db.Integer, db.ForeignKey("tag.id"), primary_key=True),
db.Column("discussion_id", db.Integer, db.ForeignKey("discussion.id"), primary_key=True)
)
# discussion_comments = db.Table("discussion_comments",
# db.Column("comment_id", db.Integer, db.ForeignKey("comment.id"), primary_key=True),
# db.Column("discussion_id", db.Integer, db.ForeignKey("discussion.id"), primary_key=True)
# )
# replies = db.Table("replies",
# db.Column("reply_id", db.Integer, db.ForeignKey("reply.id"), primary_key=True),
# db.Column("discussion_id", db.Integer, db.ForeignKey("discussion.id"), primary_key=True)
# )
class Discussion(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String, nullable=False)
content = db.Column(db.String)
author_id = db.Column(db.Integer, db.ForeignKey("user.id"))
tags = db.relationship("Tag", secondary=tags, lazy="dynamic", backref=db.backref("discussions", lazy=True))
# comments = db.relationship("Comment", secondary=discussion_comments, lazy="subquery", backref=db.backref("discussions", lazy=True))
# replies = db.relationship("Reply", secondary=replies, lazy="subquery", backref=db.backref("discussions", lazy=True))
comments = db.relationship("Comment", backref="discussion", lazy="dynamic")
replies = db.relationship("Reply", backref="discussion", lazy="dynamic")
def __repr__(self) -> str:
return '<Discussion %s>' % self.title
class Tag(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String)
class Comment(db.Model):
id = db.Column(db.Integer, primary_key=True)
content = db.Column(db.String)
to_discussion = db.Column(db.Boolean, default=False)
author_id = db.Column(db.Integer, db.ForeignKey("user.id"))
discussion_id = db.Column(db.Integer, db.ForeignKey("discussion.id"))
reply_id = db.Column(db.Integer, db.ForeignKey("reply.id"))
# reply_comments = db.Table("reply_comments",
# db.Column("comment_id", db.Integer, db.ForeignKey("comment.id"), primary_key=True),
# db.Column("reply_id", db.Integer, db.ForeignKey("reply.id"), primary_key=True)
# )
class Reply(db.Model):
id = db.Column(db.Integer, primary_key=True)
content = db.Column(db.String)
author_id = db.Column(db.Integer, db.ForeignKey("user.id"))
discussion_id = db.Column(db.Integer, db.ForeignKey("discussion.id"))
comments = db.relationship("Comment", lazy="dynamic", backref="reply")
谢谢!!
发生递归是因为,例如,如果您尝试转储用户信息,则每个用户将包含其所有相关讨论(因为这一行:
discussions = ma.List(ma.Nested(lambda: DiscussionSchema(exclude=("author",))))
然后每个讨论模式将再次转储讨论用户.. 评论和回复也会发生这种情况。
首先从用户模式中删除这三个嵌套模式列表,当您有关于用户的查询时,您不应该转储所有数据库信息。
同样,从其他模型中删除所有嵌套架构列表(例如,从 DiscussionSchema 中删除作者、评论和回复)。