处理棉花糖模式的多个变体
Handling multiple variants of a marshmallow schema
我有一个简单的 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
对象table(一份报告有很多样本)
- 创建新报告时(例如
POST /reports
),我希望用户提供除report_id
(将生成)、report_hash
和[=之外的所有报告字段19=](将在现场计算),我还希望他们在上传时包含所有相关的 Sample
对象。
我怎样才能合理地维护这个模式的 3 个(或更多)版本?我应该:
- 有 3 个独立的
ModelSchema
子类?例如AggregateReportSchema
、SingleReportSchema
和 UploadReportSchema
?
- 有一个 mega-
ModelSchema
,它包含我在这个模式中可能想要的所有字段,然后我使用 exclude
参数 in the constructor 动态地从中减去字段?例如ReportSchema(exclude=[])
?
- 或者我应该使用继承并定义一个
class ReportBaseSchema(ModelSchema)
,而其他模式对其进行子类化以添加其他字段(例如 class UploadReportSchema(ReportBaseSchema)
)?
- 还有别的吗?
自问这个问题以来,我已经使用 Marshmallow 做了很多工作,所以希望我能解释一下。
我的经验法则是:尽可能多地使用架构构造函数(选项 #2),只有在绝对必要时才求助于继承(选项 #3)。 切勿使用选项 #1,因为这会导致不必要的重复代码。
模式构造方法很棒,因为:
- 你写的代码最少
- 您永远不必重复逻辑(例如验证)
- 架构构造函数的
only
、exclude
、partial
和 unknown
参数为您提供了足够的能力来自定义各个架构(see the documentation)
- 架构子类可以向架构构造函数添加额外的设置。例如 marshmallow-jsonapi addds
include_data
,它可以让您控制每个相关资源 return 的数据量
我原来的post是使用schema constructor就够了的情况。您应该首先定义一个包含所有可能相关字段的模式,包括可能是 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())
我有一个简单的 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
对象table(一份报告有很多样本) - 创建新报告时(例如
POST /reports
),我希望用户提供除report_id
(将生成)、report_hash
和[=之外的所有报告字段19=](将在现场计算),我还希望他们在上传时包含所有相关的Sample
对象。
我怎样才能合理地维护这个模式的 3 个(或更多)版本?我应该:
- 有 3 个独立的
ModelSchema
子类?例如AggregateReportSchema
、SingleReportSchema
和UploadReportSchema
? - 有一个 mega-
ModelSchema
,它包含我在这个模式中可能想要的所有字段,然后我使用exclude
参数 in the constructor 动态地从中减去字段?例如ReportSchema(exclude=[])
? - 或者我应该使用继承并定义一个
class ReportBaseSchema(ModelSchema)
,而其他模式对其进行子类化以添加其他字段(例如class UploadReportSchema(ReportBaseSchema)
)? - 还有别的吗?
自问这个问题以来,我已经使用 Marshmallow 做了很多工作,所以希望我能解释一下。
我的经验法则是:尽可能多地使用架构构造函数(选项 #2),只有在绝对必要时才求助于继承(选项 #3)。 切勿使用选项 #1,因为这会导致不必要的重复代码。
模式构造方法很棒,因为:
- 你写的代码最少
- 您永远不必重复逻辑(例如验证)
- 架构构造函数的
only
、exclude
、partial
和unknown
参数为您提供了足够的能力来自定义各个架构(see the documentation) - 架构子类可以向架构构造函数添加额外的设置。例如 marshmallow-jsonapi addds
include_data
,它可以让您控制每个相关资源 return 的数据量
我原来的post是使用schema constructor就够了的情况。您应该首先定义一个包含所有可能相关字段的模式,包括可能是 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())