У меня есть модель пользователя и роли с отношением «многие ко многим»
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. Также работает POSTing пользователя с новыми объектами роли, встроенными в запрос. То, что я не смог выяснить, это как 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."
]
}
}
Я могу сказать, что в схеме что-то отсутствует, чтобы можно было получать идентификаторы, а не объекты, и я подозреваю, что мне нужно использовать 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)
Спасибо за любую помощь.