首页 > 解决方案 > 调用 Python marshmellow sqlAlchemy 对象以将其序列化为 ino json 时超出了最大递归深度

问题描述

我正在用 Python 制作后端 API,并将 Marshmellow sqlAlchemy 与正在 AWS 无服务器本地主机上测试的 MySql 数据库一起使用我正在使用 Postman 测试我的 API

当我发送 Get Request 如下 http://localhost:8000/accounts/admin/individuals/test 调用 AWS lembda 函数时,我收到以下错误

{
    "error": {
        "status": 500,
        "message": "maximum recursion depth exceeded while calling a Python object",
        "type": "Internal Server Error"
    }
} 

我的代码在这里

@session_committer
# pylint: disable=unused-argument
def test_account_indiv(event: Dict[str, Any], context: Dict[str, Any],\
                        session: Session = Sess) -> Dict[str, Any]:
    """_"""
    #account_id = event['pathParameters']['account_id']
    res = session.query(User).filter_by(account_type="INDIVIDUAL", is_deleted=0).all()
    
    only = ("id", "created_date", "full_name", "status", "addresses", "email", "phone_number",
            "account_type", "http_referrer", "employment_type", "account_source",
            "default_address_id", "change_logs", "tags.tag", "tags.hex_color", "order_customers",)
    schema = UserSchema()
    
    for x in range(len(res)-1):
        res[x]=schema.dump(res[x])
    
    #response = schema.dump(res[1])
    print(res)
    return Response(data= res, status_code=200).to_json()

这是上面使用的我的 Alchemy ORM 模型及其在 user.py 中的架构:

# pylint: disable=missing-function-docstring
from giddyops.models.base import Base
from marshmallow import fields, validate
from marshmallow_sqlalchemy import SQLAlchemySchema
from sqlalchemy import Column, Integer, String, TIMESTAMP, Text, ForeignKey, Date, Enum, Index
from sqlalchemy.ext.hybrid import hybrid_property
from sqlalchemy.orm import relationship
from sqlalchemy.sql import text

# Enums
PAYMENT_TERMS_ENUM = ['ON_RECEIPT', 'NET_5', 'NET_10', 'NET_15', 'NET_30']
EMPLOYMENT_TYPE_ENUM = ['CONTRACTOR', 'EMPLOYEE']
SOURCE_ENUM = ['WEBSITE', 'CALL', 'EMAIL', 'CHAT', 'AD', 'BLOG', 'REFERRAL', 'OTHER']
ACCOUNT_TYPE_ENUM = ['INDIVIDUAL', 'ORGANIZATION', 'ORGANIZATION_MEMBER']
STATUS_ENUM = ['ACTIVE', 'INACTIVE']


class User(Base):
    __tablename__ = 'users'
    schema = 'giddyops'
    filterable_fields = [
        'status',
        'http_referrer',
        'account_type',
        'payment_terms',
        'employment_type',
        'account_source',
        ['roles', 'position'],
        ['default_region', 'name'],
        ['default_address', 'city'],
        ['default_address', 'country'],
        ['default_address', 'postal_code'],
        ['default_address', 'state']
    ]
    foreign_columns = ['default_region', 'default_address']
    load_only_columns = ['is_deleted', 'roles', 'organization_id']
    computed_fields = ['full_name']
    graph_data = {
        'WORKERS': [
            [['count'], 'string', 'y', 'Count', [], False, []],
            [['full_name'], 'string', 'filter', 'Name', [], False, []],
            [['created_date'], 'datetime', 'x', 'Creation Date', [], False, []],
            [[['default_region', 'name']], 'string', 'filter', 'Region', [], True, []],
            [['total_paid_amount'], 'double', 'y', 'Total Paid $amnt', [], False, ['created_date']],
            [['status'], 'string', 'filter', 'Status', [], True, []],
            [['email'], 'string', 'filter', 'email', [], False, []],
            [['birth_date'], 'datetime', 'filter', 'Birth Date', [], False, []],
            [['http_referrer'], 'string', 'filter', 'Referrer', [], False, []],
            [['phone_number'], 'string', 'filter', 'Phone Number', [], False, []],
            [['employment_type'], 'string', 'filter', 'Employment Type', [], True, []],
            [['hire_date'], 'datetime', 'x', 'Hire Date', [], False, []],
            [['termination_date'], 'datetime', 'x', 'Term Date', [], False, []],
        ],
        'ACCOUNTS': [
            [['count'], 'string', 'y', 'Count', [], False, []],
            [['full_name'], 'string', 'filter', 'Name', [], False, []],
            [['created_date'], 'datetime', 'x', 'Creation Date', [], False, []],
            [['account_type'], 'string', 'filter', 'Account Type', [], False, []],
            [[['default_region', 'name']], 'string', 'filter', 'Region', [], True, []],
            [['total_sales_amount'], 'double', 'y', 'Total Sales $amnt', [], False, []],
            [['status'], 'string', 'filter', 'Status', [], True, []],
            [['email'], 'string', 'filter', 'Email', [], False, []],
            [['phone_number'], 'string', 'filter', 'Phone Number', [], False, []],
            [['account_source'], 'string', 'filter', 'Source', [], True, []],
            [['account_type'], 'string', 'filter', 'Type', [], True, []],
            [['payment_terms'], 'string', 'filter', 'Payment Due', [], True, []],
        ]
    }

    # Server Generated Fields
    id = Column(Integer, primary_key=True)
    created_date = Column(TIMESTAMP, nullable=False, server_default=text('CURRENT_TIMESTAMP'))
    modified_date = Column(TIMESTAMP, nullable=False,
                           server_default=text('CURRENT_TIMESTAMP'),
                           server_onupdate=text('CURRENT_TIMESTAMP'))

    # Required Fields
    email = Column(String(64), nullable=False)
    account_type = Column(Enum(*ACCOUNT_TYPE_ENUM), nullable=False)

    # Optional Fields (default value provided)
    status = Column(Enum(*STATUS_ENUM), nullable=False, default=STATUS_ENUM[0])
    is_deleted = Column(Integer, nullable=False, default=0)
    payment_terms = Column(Enum(*PAYMENT_TERMS_ENUM), nullable=False, default=PAYMENT_TERMS_ENUM[0])

    # Nullable Fields
    first_name = Column(String(32))
    last_name = Column(String(32))
    phone_number = Column(String(32))
    birth_date = Column(Date)
    default_region_id = Column(Integer, ForeignKey('regions.id'))
    default_address_id = Column(Integer, ForeignKey('addresses.id'))
    default_preferences = Column(Text)
    http_referrer = Column(String(32))
    worker_referral_id = Column(Integer)
    stripe_user_id = Column(Integer)
    organization_id = Column(Integer)
    employment_type = Column(Enum(*EMPLOYMENT_TYPE_ENUM))
    hire_date = Column(TIMESTAMP)
    termination_date = Column(TIMESTAMP)
    account_source = Column(Enum(*SOURCE_ENUM))
    auth0_user_id = Column(String(64))

    # Computed Fields
    @hybrid_property
    def full_name(self):
        full_name = ""

        if self.first_name is not None:
            full_name += self.first_name

        if self.last_name is not None:
            if full_name != "":
                full_name += " " + self.last_name
            else:
                full_name += self.last_name

        return full_name

    # Indices
    Index('auth0_user_id_index', "auth0_user_id", unique=True)

    # FK Relationships
    assigned_region_workers = relationship("AssignedRegionWorker", back_populates="customer")
    attachments = relationship("Attachment", back_populates="user")
    applicants = relationship("Applicant", back_populates="user")
    addresses = relationship("Address", foreign_keys="Address.user_id", back_populates="user")
    change_logs = relationship("ChangeLog", secondary="user_change_log_mapper")
    default_address = relationship("Address", foreign_keys=[default_address_id])
    roles = relationship("TenantUserRole", back_populates="user")
    order_workers = relationship("Order", foreign_keys="Order.worker_id", back_populates="worker")
    order_customers = relationship("Order", foreign_keys="Order.customer_id",
                                   back_populates="customer")
    worker_region = relationship(
        "RegionWorker",
        foreign_keys="RegionWorker.worker_id",
        back_populates="worker")
    worker_ratings_received = relationship(
        "WorkerRating",
        foreign_keys="WorkerRating.worker_id",
        back_populates="worker"
    )
    worker_ratings_left = relationship(
        "WorkerRating",
        foreign_keys="WorkerRating.customer_id",
        back_populates="customer"
    )
    transactions = relationship("Transaction", back_populates="user")
    leads = relationship("Lead", back_populates="assigned_worker")
    custom_reports = relationship("CustomReport", back_populates="owner")
    user_article_statuses = relationship("UserArticleStatus", back_populates="user")
    user_course_assignments = relationship("UserCourseAssignment", back_populates="user")
    user_store_service_areas = relationship(
        "UserStoreServiceArea",
        foreign_keys="UserStoreServiceArea.worker_id",
        back_populates="user")
    results = relationship("QuizResult", back_populates="user")
    default_region = relationship("Region", foreign_keys=[default_region_id],
                                  back_populates="users")
    user_service_area_schedules = relationship(
        "UserServiceAreaSchedule",
        foreign_keys="UserServiceAreaSchedule.worker_id",
        back_populates="worker")
    tags = relationship("Tag", secondary="user_tag_mapper")


class UserSchema(SQLAlchemySchema):
    class Meta:
        model = User
        load_instance = True

    # Server Generated Fields
    id = fields.Integer()
    created_date = fields.DateTime()
    modified_date = fields.DateTime()

    # Required Fields
    email = fields.Email(required=True, validate=validate.Length(min=1, max=64))
    account_type = fields.String(required=True, validate=validate.OneOf(ACCOUNT_TYPE_ENUM))

    # Optional Fields
    status = fields.String(validate=validate.OneOf(STATUS_ENUM))
    is_deleted = fields.Integer(load_only=True)
    payment_terms = fields.String(validate=validate.OneOf(PAYMENT_TERMS_ENUM))

    # Nullable Fields
    first_name = fields.String(validate=validate.Length(min=1, max=32))
    last_name = fields.String(validate=validate.Length(min=1, max=32))
    phone_number = fields.String(validate=validate.Regexp(r'^\+[1-9]\d{1,14}$'))
    birth_date = fields.Date()
    default_region_id = fields.Integer()
    default_address_id = fields.Integer()
    default_preferences = fields.String(validate=validate.Length(min=1))
    http_referrer = fields.String(validate=validate.Length(min=1, max=32))
    worker_referral_id = fields.Integer()
    stripe_user_id = fields.Integer()
    organization_id = fields.Integer()
    employment_type = fields.String(validate=validate.OneOf(EMPLOYMENT_TYPE_ENUM))
    hire_date = fields.Date()
    termination_date = fields.Date()
    account_source = fields.String(validate=validate.OneOf(SOURCE_ENUM))
    auth0_user_id = fields.String(validate=validate.Length(min=1, max=64))

    # Computed Fields
    full_name = fields.String()

    # FK Fields
    roles = fields.List(fields.Nested("TenantUserRoleSchema", only=("position", "role",)))
    default_region = fields.Nested("RegionSchema")
    default_address = fields.Nested("AddressSchema")
    change_logs = fields.List(fields.Nested("ChangeLogSchema"))
    tags = fields.List(fields.Nested("TagSchema"))
    assigned_region_workers = fields.List(fields.Nested("AssignedRegionWorkerSchema"))
    addresses = fields.List(fields.Nested("AddressSchema"))
    order_customers = fields.List(fields.Nested("OrderSchema", exclude=("customer", "worker",
                                                                        "customer_address",
                                                                        "region",
                                                                        "tenant")))

抱歉,我无法向我的一周英语解释你

基本上我不知道如何将 marshmellow-sqlAlchemy 对象序列化为 Json,因为我正在从要序列化的查询会话中返回用户对象列表

标签: pythonjsonserializationsqlalchemy

解决方案


推荐阅读