server_sqlalchemy 的 spyne 多对多版本

spyne many-to-many version of server_sqlalchemy

我修改了 server_sqlalchemy 示例以启用 multi table 如下 permissions = Array(Permission).store_as(table(multi=True)) 并添加了类似 get_permissionput_permissionget_all_permission 的方法get_userput_userget_all_user.

在客户端,我使用如下的 suds 客户端:

from suds.client import Client
c = Client('http://localhost:8000/?wsdl')

#create two permissions
p = c.factory.create('Permission')
p.application = 'usermgr'
p.operation = 'modify'
p.id = c.service.put_permission(p)

q = c.factory.create('Permission')
q.application = 'accountmgr'
q.operation = 'read'
q.id = c.service.put_permission(q)

#create two users
u = c.factory.create('User')
u.user_name = 'abcd'
u.full_name = 'abcd xyz'
u.email = 'abcd@xyz.com'
u.permissions = c.factory.create('PermissionArray')
u.permissions.Permission = [p,q]
u.id = c.service.put_user(u)

v = c.factory.create('User')
v.user_name = 'dcba'
v.full_name = 'dcba zyx'
v.email = 'dcba@zyx.com'
v.permissions = c.factory.create('PermissionArray')
v.permissions.Permission = [p,q] #note the same p,q used in u
v.id = c.service.put_user(v)

put_user(v) 由于

而失败

sqlalchemy.exc.InvalidRequestError: This Session's transaction has been rolled back due to a previous exception during flush. To begin a new transaction with this Session, first issue Session.rollback(). Original exception was: (sqlite3.IntegrityError) UNIQUE constraint failed: permission.id [SQL: 'INSERT INTO permission (id, operation, application) VALUES (?, ?, ?)'] [parameters: ((3, 'modify', 'usermgr'), (4, 'read', 'accountmgr'))]

很明显,代码试图将 p,q gain 插入权限 table 但失败了。 multi table 不应该只插入 user_permissions table 吗?如果没有,我如何实现客户端中显示的所需行为?

谢谢

TL;DR:使用此 post 底部建议的第一个解决方法。


好的,我知道这会是个问题。

这是这里发生的事情:

  1. 您成功插入了两个权限对象,获取了它们的服务器生成的id。
  2. 您创建一个用户对象并将新插入的权限放入用户实例的权限数组中。
  3. 由于 put_user 作为使用新会话的单独请求进入,sqlalchemy 不记得这两个权限对象已经在数据库中并尝试再次插入它们。
  4. 由于这些权限的主键已经存在,您会收到该错误。

所以这与在权限数组中包含 multi=True 无关。

SQLAlchemy 中的关系默认为 "owner" 关系。这里你需要一个 "user" 关系。

即在这种情况下,User 实例没有 "own" 权限,它们只是 "use" 权限。例如删除用户时需要删除none的权限

我不知道用 SQLAlchemy 定义 "user" 关系的任何方法。您需要将 "used" 对象放入会话中,唯一的方法是 selecting 它们——这是低效的。这是 SQLAlchemy 的作者关于这个主题的回答:

从 2.12 开始,Spyne 还不支持将关系标记为 "owner/user" 关系——我只是没有足够的时间研究 SQLAlchemy 的内部机制来提出一个优雅的实现。


所以我为您准备了两个解决方法:

  1. user.permissions = [session.query(Permission).filter_by(id=perm.id).one() for perm in user.permissions]

    非常低效,但单行。为每个传入的权限对象运行 select。因为它使用 .one() 它将确保客户端只能添加已经存在的权限。请注意,Spyne 将优雅地处理 NotFoundException 和 return 客户端错误。

  2. 手动插入关系行的高效版本。

    permissions = user.permissions
    user.permissions = []
    session.add(user)
    session.flush()
    for perm in permissions:
        session.connection().execute("insert into user_permission "
                         "(user_id, permission_id) values (%s, %s)",
                                                   perm.id, user.id)
    

    请注意

    我。这可以进一步优化以使用发出多行的单个插入。

    二。如果权限 ID 无效,这将引发 Spyne 不知道的外键错误。这意味着客户端将收到服务器错误,并认为您这边有问题,而错误实际上是他的错。因此,如果您关心重新调整正确的异常,则需要捕获数据库异常,确保它是针对指向权限 table 的特定外键的约束验证,并引发一个客户端错误,说明类似以下内容"Unknown permission id".

如果您愿意,可以加入 http://lists.spyne.io/listinfo/people 进一步讨论。