【问题标题】:Generating marshmallow schema automatically with JSON serializable enums使用 JSON 可序列化枚举自动生成棉花糖模式
【发布时间】:2020-12-21 03:32:15
【问题描述】:

创建与我的模型相同的棉花糖模式的日子已经一去不复返了。我发现 this excellent answer 解释了如何使用简单的装饰器从我的 SQA 模型自动生成模式,因此我实现了它并将已弃用的 ModelSchema 替换为较新的 SQLAlchemyAutoSchema

def add_schema(cls):
    class Schema(SQLAlchemyAutoSchema):
        class Meta:
            model = cls
    cls.Schema = Schema
return cls

这很好用...直到我碰到一个带有血腥Enum的模特。

错误:Object of type MyEnum is not JSON serializable

我在网上搜索,找到了this useful answer

但我想将它作为装饰器的一部分来实现,以便它也可以自动生成。换句话说,当使用add_schema 装饰器生成模式时,我想用EnumField(TheEnum, by_value=True) 自动覆盖我的模型中的所有枚举;这样我就不必手动覆盖所有字段。

最好的方法是什么?

【问题讨论】:

    标签: python enums sqlalchemy marshmallow


    【解决方案1】:

    这是我的解决方案:

    from marshmallow import validate
    from marshmallow_sqlalchemy import SQLAlchemyAutoSchema
    from marshmallow_enum import EnumField
    from enum import Enum
    
    def add_schema(cls):
        class Schema(SQLAlchemyAutoSchema):
            class Meta:
                model = cls
    
        fields = Schema._declared_fields
    
        # support for enum types
        for field_name, field_details in fields.items():
            if len(field_details.validate) > 0:
                enum_list = field_details.validate[0].choices
                enum_dict = {enum_list[i]: enum_list[i] for i in range(0, len(enum_list))}
                enum_clone = Enum(field_name.capitalize(), enum_dict)
                fields[field_name] = EnumField(enum_clone, by_value=True, validate=validate.OneOf(enum_list))
    
        cls.Schema = Schema
        return cls
    

    这个想法是遍历 Schema 中的字段并找到那些具有验证的字段(通常是枚举)。从那里我们可以提取一个选项列表,然后可以用来从头开始构建一个枚举。最后,我们用新的EnumField 覆盖架构字段。

    请随时改进答案!

    【讨论】:

      【解决方案2】:

      我发现最初建议的对枚举类型的支持仅在OneOffield_details 中存在的唯一验证类时才有效。我添加了一些参数解析(通过在将结果_repr_args()OneOf 字符串化后查找choices 的基本方式)来检查验证类,以希望使这个实现更普遍可用:

      def add_schema(cls):
          class Schema(ma.SQLAlchemyAutoSchema):
              class Meta:
                  model = cls
      
          fields = Schema._declared_fields
      
          # support for enum types
          for field_name, field_details in fields.items():
              if len(field_details.validate) > 0:
                  check = str(field_details.validate[0]._repr_args)
                  if check.__contains__("choices") :
                      enum_list = field_details.validate[0].choices
                      enum_dict = {enum_list[i]: enum_list[i] for i in range(0, len(enum_list))}
                      enum_clone = Enum(field_name.capitalize(), enum_dict)
                      fields[field_name] = EnumField(enum_clone, by_value=True, validate=validate.OneOf(enum_list))
      
          cls.Schema = Schema
          return cls
      

      感谢jgozal 提供的初始解决方案,因为我现在的项目确实需要这个线索。

      【讨论】:

      • 您的答案可以通过额外的支持信息得到改进。请edit 添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。你可以找到更多关于如何写好答案的信息in the help center
      猜你喜欢
      • 2017-11-26
      • 2020-06-25
      • 1970-01-01
      • 1970-01-01
      • 2018-11-12
      • 1970-01-01
      • 2016-09-01
      • 2017-08-10
      • 1970-01-01
      相关资源
      最近更新 更多