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

Mig*_*ell 5 python sqlalchemy flask 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)
Run Code Online (Sandbox Code Playgroud)

然后我有相应的棉花糖-SQLAlchemy模式:

class ReportSchema(ModelSchema):
    class Meta:
        model = Report
Run Code Online (Sandbox Code Playgroud)

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

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

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

  • 有3个单独的ModelSchema子类?例如AggregateReportSchema,SingleReportSchema和UploadReportSchema?
  • 有一个mega- ModelSchema包含我在该模式中可能想要的所有字段,然后使用构造exclude函数中的参数即时从中减去字段?例如ReportSchema(exclude=[])?
  • 还是应该使用继承并定义一个class ReportBaseSchema(ModelSchema),其他模式对此进行子类添加其他字段(例如class UploadReportSchema(ReportBaseSchema))?
  • 还有吗

Mig*_*ell 8

自从问这个问题以来,我已经使用棉花糖做了很多工作,所以希望我能解释一下。

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

模式构造函数方法很棒,因为:

  • 你最终会编写最少的代码
  • 您永远不必重复逻辑(例如验证)
  • only模式构造函数的 、、exclude和partial参数unknown为您提供了足够的能力来自定义各个模式(请参阅文档)。
  • 模式子类可以向模式构造函数添加额外的设置。例如marshmallow-jsonapi addds 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())
Run Code Online (Sandbox Code Playgroud)