python - 反序列化多对多 PUT 操作的 id 列表

标签 python marshmallow flask-marshmallow marshmallow-sqlalchemy flask-smorest

我有一个具有多对多关系的用户和角色模型

class User(BaseModel, TimestampableMixin):

    username = Column(String(MEDIUM_STRING_LENGTH), nullable=False, unique=True)

    roles = relationship('Role', secondary='user_roles', back_populates='users')

class Role(BaseModel, TimestampableMixin):

    label = Column(String(MEDIUM_STRING_LENGTH), nullable=False, unique=True)

    users = relationship('User', secondary='user_roles', back_populates='roles')

class UserRole(BaseModel, TimestampableMixin):

    user_id = Column(ForeignKey('users.id', ondelete=CASCADE, onupdate=CASCADE), nullable=False, index=True)
    role_id = Column(ForeignKey('roles.id', onupdate=CASCADE), nullable=False, index=True)

然后,我为用户定义了嵌套角色的架构。

class RoleSchema(BaseSchema):
    class Meta:
        model = models.Role

class UserSchema(BaseSchema):
    class Meta:
        model = models.User

    roles = fields.Nested('RoleSchema', many=True, exclude=['users'])

对于序列化来说,当角色对象列表包含在用户 GET 请求中时,这非常有效。同样有效的方法是使用请求中嵌入的新角色对象来发布用户。我无法弄清楚如何 POST/PUT 现有角色 ids 列表,而不是创建新对象。 例如,此请求有效:

{
    "username": "testuser12",
    "roles": [
        {
            "label": "newrole"
        }
    ]
}

回应:

{
  "createdTime": "2020-02-06T19:13:29Z",
  "id": 4,
  "modifiedTime": "2020-02-06T19:13:29Z",
  "roles": [
    {
      "createdTime": "2020-02-06T19:13:29Z",
      "id": 2,
      "label": "newrole",
      "modifiedTime": "2020-02-06T19:13:29Z"
    }
  ],
  "username": "testuser12"
}

但是这些请求都不起作用:

{
    "username": "testuser13",
    "roles": [
        1
    ]
}

{
    "username": "testuser13",
    "roles": [
        {
            "id": 1
        }
    ]
}

我收到这样的回复:

{
  "errors": {
    "error": [
      "Unprocessable Entity"
    ],
    "message": [
      "The request was well-formed but was unable to be followed due to semantic errors."
    ]
  }
}

我可以看出我在架构中缺少一些能够获取 id 而不是对象的东西,并且我怀疑我需要使用 dump_only/load_only 以及可能需要使用单独的 PUT 架构。但是,我无法在网上找到此用例的示例。

提到我正在使用 flask-smorest 进行请求验证和模式参数摄取也可能会有所帮助。

@B_API.route('/user/<user_id>')
class UserByIdResource(MethodView):

    @B_API.response(schemas.UserSchema)
    def get(self, user_id):
        """
        Get a single user by id
        """
        return models.User.query.get(user_id)

    @B_API.arguments(schemas.UserSchema)
    @B_API.response(schemas.UserSchema)
    def put(self, updated_user, user_id):
        """
        Update fields of an existing user
        """
        models.User.query.get_or_404(user_id, description=f'User with id {user_id} not found')
        user = updated_user.update_with_db(user_id)
        return user

update_with_db 看起来像:

    def update_with_db(self, id: int):
        self.id = id
        DB.session.merge(self)
        DB.session.commit()
        return self.query.get(id)

感谢您的帮助。

最佳答案

我最终通过创建一个单独的 PUT 模式并使用 Pluck 作为角色 ID 来解决这个问题。

class UserSchema(BaseModelSchema):
    class Meta:
        model = models.User

    roles = fields.Nested('RoleSchema', many=True, exclude=['users'])


class UserPutSchema(BaseModelSchema):
    class Meta:
        model = models.User

    roles = fields.Pluck('RoleSchema', 'id', many=True)

这些架构是使用以下资源实现的,采用 PUT 架构进行输入验证,并返回标准用户架构以符合 GET 和 POST 响应。这似乎工作得很好,唯一的缺点是必须定义一个单独的模式。

    @B_API.arguments(schemas.UserPutSchema(partial=True))
    @B_API.response(schemas.UserSchema)
    def put(self, updated_user, user_id):
        """
        Update fields of an existing user
        """
        get_by_id(models.User, user_id)
        user = updated_user.update_with_db(user_id)
        return user

我现在可以发出 PUT 请求,例如

PUT /user/1
{
    "roles": [
        1,
        2
    ]
}

并得到类似的响应

{
  "createdTime": "2020-02-20T01:33:42Z",
  "historys": [],
  "id": 1,
  "modifiedTime": "2020-02-20T01:33:42Z",
  "roles": [
    {
      "createdTime": "2020-02-19T23:43:06Z",
      "description": null,
      "historys": [],
      "id": 1,
      "key": "TEST_ROLE_1",
      "label": "role 1",
      "modifiedTime": "2020-02-19T23:43:06Z"
    },
    {
      "createdTime": "2020-02-19T23:43:06Z",
      "description": null,
      "historys": [],
      "id": 2,
      "key": "TEST_ROLE_2",
      "label": "role 2",
      "modifiedTime": "2020-02-19T23:43:06Z"
    }
  ],
  "username": "testuser1"
}

这就是我的目标。

关于python - 反序列化多对多 PUT 操作的 id 列表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60102126/

相关文章:

Python 和 Node.js gyp 错误

python - 如何在 C++ 中为 Cython 中的两种类型使用模板化函数?

python - 更改 python Flask/marshmallow 中的 json 属性顺序

python 棉花糖 : Dict validation Error

python - 顶级棉花糖模式验证

python - 如何使用 Flask-Marshmallow 处理文件上传验证?

python - 是否有办法通过 python 绑定(bind)为 Zookeeper 节点提供 NULL ACL?

Python 2.7 与 BeautifulSoup 错误 : Cannot process flags argument with a compiled pattern

python - Flask、marshmallow - 嵌套字段的问题

python - 类型错误 : _deserialize() got an unexpected keyword argument 'partial' in marshmallow