2

我对 Flask-Marshmallow / Flask-SQLAlchemy 非常陌生,我正在尝试使用烧瓶和 mysql 设置我自己的 REST API。

这是我要发布的有效负载。我希望能够只发送 ID 并排除所有其他字段:

{
    "code": "FG034",
    "product_name": "test test2",
    "description": "Description",
    "init_date": "2021-01-10",
    "init_by": {
        "id": "27bb9e1acad247618fb2c3e016ae841c"
    }
}

这是我发送该有效负载时遇到的错误:

ERROR in utils: (pymysql.err.IntegrityError) (1048, "Column 'full_name' cannot be null")
[SQL: INSERT INTO user (id, full_name, initials, hashed_password, is_active) VALUES (%(id)s, %(full_name)s, %(initials)s, %(hashed_password)s, %(is_active)s)]
[parameters: {'id': '27bb9e1acad247618fb2c3e016ae841c', 'full_name': None, 'initials': None, 'hashed_password': None, 'is_active': 1}]
(Background on this error at: http://sqlalche.me/e/13/gkpj)

这很奇怪,因为我不想在用户表中插入任何东西......

当我将所有字段放入其中时,会按预期添加:

{
    "code": "FG034",
    "product_name": "test test2",
    "description": "Description",
    "init_date": "2021-01-10",
    "init_by": {
        "id": "27bb9e1acad247618fb2c3e016ae841c",
        "initials": "EE",
        "email": "example@gmail.com",
        "full_name": "Example Exampleton",
        "is_active": true
    }
}

这是添加 FinishedGood 的结果的 GET(这是我想要的):

{
    "code": "FG034",
    "init_date": "2021-01-10",
    "description": "Description",
    "id": 3,
    "is_active": true,
    "init_by": {
        "email": "example@gmail.com",
        "id": "27bb9e1acad247618fb2c3e016ae841c",
        "is_active": true,
        "full_name": "Example Exampleton",
        "initials": "EE"
    },
    "product_name": "test test2"
}

也许我完全错误地使用了这个,但谁能告诉我:

  1. 为什么它试图插入用户表?我想要的只是要加载到 init_by_id 中的主键
  2. 有没有办法像我在开始时显示的那样仅使用 ID 发布有效负载?

这是我的类和模式:

class User(db.Model):
    id = db.Column(db.CHAR(32), primary_key=True, default=uuid.uuid4().hex)
    email = db.Column(db.String(255), primary_key=True)
    full_name = db.Column(db.String(255), nullable=False)
    initials = db.Column(db.String(3), unique=True, nullable=False)
    hashed_password = db.Column(db.Text, nullable=False)
    is_active = db.Column(db.Boolean, default=True)

class UserSchema(ma.SQLAlchemySchema):
    class Meta:
        model = User
        load_instance = True
        sqla_session = db.session

    id = ma.auto_field()
    email = ma.auto_field(required=False)
    full_name = ma.auto_field(required=False)
    initials = ma.auto_field(required=False)
    is_active = ma.auto_field(required=False)

class FinishedGood(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    code = db.Column(db.String(255), unique=True, nullable=False)
    product_name = db.Column(db.String(255), nullable=False)
    description = db.Column(db.Text)
    init_date = db.Column(db.Date, nullable=False)
    init_by_id = db.Column(db.CHAR(32), db.ForeignKey(User.id), nullable=False)
    init_by = db.relationship(User)
    is_active = db.Column(db.Boolean, default=True)

class FinishedGoodSchema(ma.SQLAlchemyAutoSchema):
    class Meta:
        model = FinishedGood
        load_instance = True
        sqla_session = db.session

    init_by = ma.Nested(UserSchema)

最后是我的发布方法:

def post_finished_good():
    data = request.get_json(force=True)
    fg = FinishedGoodSchema().load(data)
    db.session.add(fg)
    db.session.commit()
    return FinishedGoodSchema().jsonify(fg), 201
4

2 回答 2

1

我也是 Marshmallow 的新手,所以这可能不是最佳解决方案,但我设法使用@pre_load装饰器找到了解决方法:

@pre_load
def pre_load_process(self, data, **kwargs):
  id = data.get('id')
  if id is None:
    return data

  user = User.query.filter(User.id == id).first()
  if user is None:
    return data

  merged_data = UserSchema().dump(user)
  merged_data.update(data)

  return merged_data

本质上,将此方法添加到您的UserSchema类可以使其id仅从示例有效负载中反序列化。如果还包括其他字段id,它们将覆盖从数据库中提取的值。

如果您需要User防止. FinishedGood_ _FinishedGoodSchema@pre_loadidinit_by

这似乎不是一个特别奇特的用例,所以我很惊讶在网上找到相关示例有多么困难——我发现的只是你三个月大的问题没有答案......就像我说的,我我也是使用这个库的初学者,并希望从棉花糖专家那里得到一些背景信息。当这种表面上简单的事情实施起来却令人头疼时,这让我担心我的整个方法被误解了。

于 2021-04-23T16:06:31.320 回答
0

TLDR:将 init_by 字段更改为init_by = autofield(). 然后将init_by成员加载为:"init_by": "27bb9e1acad247618fb2c3e016ae841c". 为序列化创建一个单独的模式init_by = Nested(UserSchema)以生成所需的输出。

class User(db.Model):
    # columns omitted for brevity

class UserSchema(ma.SQLAlchemySchema):
    # fields omitted for brevity

class FinishedGood(db.Model):
    init_by = db.relationship(User)
    # columns omitted for brevity

class FinishedGoodSchema(ma.SQLAlchemyAutoSchema):
    class Meta:
        model = FinishedGood
        load_instance = True
        sqla_session = db.session

        # default is False
        load_relationships = True

    # or
    init_by = autofield()

    # remove this: nested fields seem to apply only for serialization and for loading a new instance 
    # init_by = ma.Nested(UserSchema)

有效载荷:

{
    "code": "FG034",
    "product_name": "test test2",
    "description": "Description",
    "init_date": "2021-01-10",
    "init_by": "27bb9e1acad247618fb2c3e016ae841c"
}

我尚未对此进行测试,但请随时发表评论以进行澄清。

于 2021-07-29T03:01:11.827 回答