处理棉花糖模式的多个变体

时间:2019-07-29 08:22:36

标签: python flask sqlalchemy marshmallow

我有一个简单的Flask-SQLAlchemy模型,我正在为其编写REST API:

class Report(db.Model, CRUDMixin):
    report_id = Column(Integer, primary_key=True)
    user_id = Column(Integer, ForeignKey('users.user_id'), index=True)
    report_hash = Column(Unicode, index=True, unique=True)
    created_at = Column(DateTime, nullable=False, default=dt.datetime.utcnow)
    uploaded_at = Column(DateTime, nullable=False, default=dt.datetime.utcnow)

然后我有相应的Marshmallow-SQLAlchemy模式:

class ReportSchema(ModelSchema):
    class Meta:
        model = Report

但是,在我的其余API中,我需要能够转储和加载此模型的稍微不同的变体:

  • 转储所有报告(例如GET /reports)时,我要转储上述所有字段。
  • 在转储单个报告(例如GET /reports/1)时,我想转储所有这些数据以及所有关联关系,例如Sample表中的关联sample对象(一个报告包含许多示例)
  • 在创建新报告(例如POST /reports)时,我希望用户提供除report_id(将生成),report_hash和{{1}之外的所有报告字段。 }(将在现场进行计算),并且我希望他们在其上载中包括所有关联的uploaded_at对象。

如何合理维护此架构的3个(或更多)版本?我应该:

  • 是否有3个单独的Sample子类?例如ModelSchemaAggregateReportSchemaSingleReportSchema
  • 是否有一个mega-UploadReportSchema包含该模式中我可能想要的所有字段,然后使用ModelSchema自变量in the constructor即时从中减去字段?例如exclude
  • 还是应该使用继承并定义一个ReportSchema(exclude=[]),其他模式对此进行子类化以添加其他字段(例如class ReportBaseSchema(ModelSchema))?
  • 还有什么?

1 个答案:

答案 0 :(得分:0)

问了这个问题,我已经用棉花糖做了很多工作,希望我能有所解释。

我的经验法则是:尽可能多地使用模式构造函数(选项#2),并且在绝对必要时仅求助于继承(选项#3)。请使用选项#1,因为这将导致不必要的重复代码。

架构构造器方法很棒,因为:

  • 您最终只编写了最少的代码
  • 您不必重复逻辑(例如验证)
  • 模式构造函数的onlyexcludepartialunknown参数使您有足够的能力自定义单个模式(see the documentation
  • 架构子类可以向架构构造器添加其他设置。例如,marshmallow-jsonapi添加了include_data,它使您可以控制为每个相关资源返回的数据量

我的原始帖子是使用架构构造函数就足够了。您首先应该定义一个包含所有可能相关字段的架构,包括可能是Nested字段的关系。然后,如果有时您不想在响应中包含相关资源或多余的字段,则可以在该视图方法中简单地使用Report(exclude=['some', 'fields']).dump()

但是,我遇到的一个使用继承更合适的例子是当我为正在生成的某些图形的参数建模时。在这里,我想要将传递到基础绘图库中的常规参数,但我希望子模式可以改进模式并使用更具体的验证:

class PlotSchema(Schema):
    """
    Data that can be used to generate a plot
    """
    id = f.String(dump_only=True)
    type = f.String()
    x = f.List(f.Raw())
    y = f.List(f.Raw())
    text = f.List(f.Raw())
    hoverinfo = f.Str()


class TrendSchema(PlotSchema):
    """
    Data that can be used to generate a trend plot
    """
    x = f.List(f.DateTime())
    y = f.List(f.Number())