0

So I'm using Flask-Marshmallows to dump the database models and convert them into Python objects to fit into my web API.

However, for some reason, the program always raises the following error:

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

I didn't see any fields that may recurse infinitely... Any ideas? Thanks!

My schemas file:

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

And my models:

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

Thank you!!

Sam Zhang
  • 320
  • 4
  • 17

1 Answers1

1

The recurssion happens because, for example, if you trying to dump your Users information, each user will include all of its related discussions (because of this line :

discussions = ma.List(ma.Nested(lambda: DiscussionSchema(exclude=("author",))))

then each discussion schema will dump the discussion user again .. and this happens also for comments and replies.

Start by removing this three nested schema lists from your user schema, you are not suppose to dump all of your DB info when you there is a query regarding your users.

Similarly, remove all nested schema lists from your other models, (e.g remove author, comments and replies from DiscussionSchema).

Nir
  • 106
  • 1
  • 7