反序列化多对多 PUT 操作的 ID 列表
Deserialize list of ids for many-to-many PUT operation
我有一个具有多对多关系的用户和角色模型
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 用户。我无法弄清楚的是如何 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"
}
这就是我的目标。
我有一个具有多对多关系的用户和角色模型
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 用户。我无法弄清楚的是如何 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"
}
这就是我的目标。