0

我修改了 server_sqlalchemy 示例以启用多表,如下所示并permissions = Array(Permission).store_as(table(multi=True))添加了方法get_permission,类似于,put_permission和.get_all_permissionget_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:由于之前刷新期间的异常,此 Session 的事务已回滚。要使用此 Session 开始新事务,首先发出 Session.rollback()。原始异常是:(sqlite3.IntegrityError) UNIQUE 约束失败:permission.id [SQL: 'INSERT INTO permission (id, operation, application) VALUES (?, ?, ?)'] [parameters: ((3, 'modify' , 'usermgr'), (4, 'read', 'accountmgr'))]

显然,代码试图将 p,q gain 插入权限表并失败。多表不应该只插入 user_permissions 表吗?如果没有,如何实现客户端中显示的所需行为?

谢谢

4

1 回答 1

0

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


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

这是这里发生的事情:

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

multi=True所以它与权限数组无关。

默认情况下,SQLAlchemy 中的关系是“所有者”关系。在这里,您需要一个“用户”关系。

即在这种情况下,User实例不“拥有”权限,它们只是“使用”它们。例如,删除用户时不需要删除任何权限。

我不知道用 SQLAlchemy 定义“用户”关系的任何方式。您需要将“使用过的”对象带入会话,而这样做的唯一方法是选择它们——这是低效的。这是 SQLAlchemy 的作者对此主题的回答:https ://stackoverflow.com/a/9674552/1520211

从 2.12 开始,Spyne 还不支持将关系标记为“所有者/用户”关系——我只是没有时间研究 SQLAlchemy 内部结构,不足以提出一个优雅的实现。


因此,我为您提供了两种解决方法:

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

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

  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)
    

    注意

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

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

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

于 2015-12-07T12:21:34.480 回答