根据官方的Marshmallow文档,它建议声明一个Schema,然后有一个单独的类来接收加载的数据,如下所示:
class UserSchema(Schema):
name = fields.Str()
email = fields.Email()
created_at = fields.DateTime()
@post_load
def make_user(self, data):
return User(**data)
但是,我的User
类看起来像这样:
class User:
def __init__(name, email, created_at):
self.name = name
self.email = email
self.created_at = created_at
这似乎不必要地重复我自己,我真的不想再写三次属性名称了。但是,我确实喜欢IDE自动完成和静态类型检查明确定义的结构。
那么,是否有根据Marshmallow模式加载序列化数据而不定义另一个类的最佳实践?
答案 0 :(得分:5)
对于vanilla Python类,没有重复字段名称的方法也没有现成的方法来定义模式的类。
例如,如果您正在使用SQLAlchemy,则可以使用marshmallow_sqlalchemy.ModelSchema
直接从模型定义架构:
from marshmallow_sqlalchemy import ModelSchema
from my_alchemy_models import User
class UserSchema(ModelSchema):
class Meta:
model = User
同样适用于使用flask_marshmallow.sqla.ModelSchema
的flask-sqlalchemy。
对于vanilla Python类,您可以定义一次字段并将其用于模式和模型/类:
USER_FIELDS = ('name', 'email', 'created_at')
class User:
def __init__(self, name, email, created_at):
for field in USER_FIELDS:
setattr(self, field, locals()[field])
class UserSchema(Schema):
class Meta:
fields = USER_FIELDS
@post_load
def make_user(self, data):
return User(**data)
答案 1 :(得分:2)
您将必须创建两个类,但是好消息是,在大多数情况下,您不必多次输入属性名称。如果使用Flask,SQLAlchemy和Marshmallow,我发现的一件事是,如果您在Column定义中定义了一些验证属性,则Marshmallow Schema将自动选择这些验证属性以及其中提供的验证。例如:
import (your-database-object-from-flask-init) as db
import (your-marshmallow-object-from-flask-init) as val
class User(db.Model):
name = db.Column(db.String(length=40), nullable=False)
email = db.Column(db.String(length=100))
created_at = db.Column(db.DateTime)
class UserSchema(val.ModelSchema):
class Meta:
model = User
在此示例中,如果您使用数据字典并将其放入UserSchema()。load(data),则在此示例中,如果名称不存在或名称长于40,则会看到错误个字符,或者电子邮件超过100个字符。除此以外的任何自定义验证都仍必须在架构中进行编码。
如果您已将模型类创建为另一个模型类的扩展并继承了它的属性,那么它也可以使用。例如,如果您希望每个类都具有创建/修改的信息,则可以将这些属性放在父模型类中,而子属性将继承这些属性及其验证参数。 Marshmallow不允许您的父模型具有模式,因此我在那里没有有关自定义验证的信息。
我知道您可能已经完成了项目,但是我希望这对遇到此问题的其他开发人员有所帮助。
相关点列表: 烧瓶(1.0.2) 烧瓶棉花糖(0.9.0) Flask-SQLAlchemy(2.3.2) 棉花糖(2.18.0) 棉花糖-sqlalchemy(0.15.0) SQLAlchemy(1.2.16)
答案 2 :(得分:1)
除非您需要反序列化为特定类或需要自定义序列化逻辑,否则您可以简单地执行此操作(改编自 https://kimsereylam.com/python/2019/10/25/serialization-with-marshmallow.html):
from marshmallow import Schema, fields
from datetime import datetime
class UserSchema(Schema):
name = fields.Str(required=True)
email = fields.Email()
created_at = fields.DateTime()
schema = UserSchema()
data = { "name": "Some Guy", "email": "sguy@google.com": datetime.now() }
user = schema.load(data)
你也可以在你的类中创建一个函数来创建一个带有验证规则的字典,虽然它仍然是多余的,但它允许你在你的模型类中保留所有东西:
class User:
def __init__(name, email, created_at):
self.name = name
self.email = email
self.created_at = created_at
@classmethod
def Schema(cls):
return {"name": fields.Str(), "email": fields.Email(), "created_at": fields.DateTime()}
UserSchema = Schema.from_dict(User.Schema)
如果您需要强类型和完整的验证功能,请考虑 flask-pydantic 或 marshmallow-dataclass。
marshmallow-dataclass 提供了许多与 marshmallow 类似的验证功能。不过它有点束缚你的手。它没有对自定义字段/多态性的内置支持(必须使用 marshmallow-union 代替),并且似乎不能很好地与像flask-marshmallow 和marshmallow-sqlalchemy 这样的堆栈包一起使用。 https://pypi.org/project/marshmallow-dataclass/
from typing import ClassVar, Type
from marshmallow_dataclass import dataclasses
from marshmallow import Schema, field, validate
@dataclass
class Person:
name: str = field(metadata=dict(load_only=True))
height: float = field(metadata=dict(validate=validate.Range(min=0)))
Schema: ClassVar[Type[Schema]] = Schema
Person.Schema().dump(Person('Bob', 2.0))
# => {'height': 2.0}
flask-pydantic 从验证的角度来看不太优雅,但提供了许多相同的功能,并且验证内置于类中。请注意,像 min/max 这样的简单验证比在棉花糖中更笨拙。就我个人而言,我更喜欢将视图/api 逻辑排除在类之外。 https://pypi.org/project/Flask-Pydantic/
from typing import Optional
from flask import Flask, request
from pydantic import BaseModel
from flask_pydantic import validate
app = Flask("flask_pydantic_app")
class QueryModel(BaseModel):
age: int
class ResponseModel(BaseModel):
id: int
age: int
name: str
nickname: Optional[str]
# Example 1: query parameters only
@app.route("/", methods=["GET"])
@validate()
def get(query:QueryModel):
age = query.age
return ResponseModel(
age=age,
id=0, name="abc", nickname="123"
)