Python 3.x 石墨烯Graphql-如何连锁突变

Python 3.x 石墨烯Graphql-如何连锁突变,python-3.x,graphql,graphene-python,graphql-mutation,Python 3.x,Graphql,Graphene Python,Graphql Mutation,我碰巧向Graphql API(Python3+Graphene)发送了两个单独的请求,以便: 创建一个对象 更新另一个对象,使其与创建的对象相关 我感觉到这可能不符合Graphql的“精神”,所以我搜索并阅读了相关内容。不幸的是,我还发现这是因为嵌套迁移不是顺序的,并且由于竞争条件,它可能会导致客户端出现难以调试的问题 我试图使用顺序根突变来实现考虑嵌套迁移的用例。请允许我向您展示一个用例和一个简单的解决方案(但可能不是好的实践)。很抱歉收到这么长的邮件 让我们想象一下,我有用户和组实体,我想

我碰巧向Graphql API(Python3+Graphene)发送了两个单独的请求,以便:

  • 创建一个对象
  • 更新另一个对象,使其与创建的对象相关
  • 我感觉到这可能不符合Graphql的“精神”,所以我搜索并阅读了相关内容。不幸的是,我还发现这是因为嵌套迁移不是顺序的,并且由于竞争条件,它可能会导致客户端出现难以调试的问题

    我试图使用顺序根突变来实现考虑嵌套迁移的用例。请允许我向您展示一个用例和一个简单的解决方案(但可能不是好的实践)。很抱歉收到这么长的邮件

    让我们想象一下,我有用户和组实体,我想从客户端表单更新一个组,这样不仅可以添加一个用户,还可以创建一个用户,如果该用户不存在,则添加到一个组中。用户具有名为uid(用户id)和groupgid(groupd id)的id,只是为了突出区别。因此,使用根突变,我设想执行如下查询:

    mutation {
        createUser(uid: "b53a20f1b81b439", username: "new user", password: "secret"){
            uid
            username
        }
    
        updateGroup(gid: "group id", userIds: ["b53a20f1b81b439", ...]){
            gid
            name
        }
    }
    
    mutation ($parent: ParentInput, $child1: ChildInput, $child2: ChildInput) {
        n1: upsertParent(data: $parent) {
            pk
            name
        }
    
        n2: upsertChild(data: $child1) {
            pk
            name
        }
    
        n3: upsertChild(data: $child2) {
            pk
            name
        }
    
        e1: setParent(parent: "n1", child: "n2") { ok }
    
        e2: setParent(parent: "n1", child: "n3") { ok }
    
        e3: addSibling(node1: "n2", node2: "n3") { ok }
    }
    
    mutation ($parent: ParentInput, $child1: ChildInput, $child2: ChildInput) {
        n1: upsertChild(data: $child1) {
            pk
            name
            siblings { pk name }
    
            parent: createParent(data: $parent) { pk name }
    
            newSibling: createSibling(data: $child2) { pk name }
        }
    }
    
    您注意到我在
    createUser
    的输入中提供了用户id。我的问题是要进行
    updateGroup
    变异,我需要新创建的用户的ID。我不知道如何在graphene中通过mutate方法解析
    updateGroup
    ,因此我设想在加载客户端表单数据时从API查询UUID。因此,在发送上述突变之前,在我的客户初次加载时,我会做如下操作:

    query {
        uuid
    
        group (gid: "group id") {
            gid
            name
        }
    }
    
    然后,我将在变异请求中使用来自此查询响应的uuid(值将是
    b53a20f1b81b439
    ,如上面的第一个脚本中所示)

    你觉得这个过程怎么样?有更好的方法吗?Python
    uuid.uuid4
    实现这一点安全吗

    提前谢谢

    -----编辑


    基于评论中的讨论,我应该提到上面的用例仅用于说明。实际上,一个用户实体可能有一个内在的唯一密钥(电子邮件、用户名),其他实体也可能有(图书的ISBN…)。我正在寻找一个通用的案例解决方案,包括可能不会显示这种自然唯一密钥的实体。

    在最初问题下的评论中有许多建议。在这个提议的最后,我会再谈一些

    我一直在思考这个问题,而且它似乎是开发人员经常遇到的问题。我得出的结论是,我们在编辑图形时可能会遗漏一些东西,即边操作。我想我们试着用节点操作来做边操作。为了说明这一点,使用类似dot(Graphviz)的语言创建的图形可能如下所示:

    digraph D {
    
      /* Nodes */
      A 
      B
      C
    
      /* Edges */
    
      A -> B
      A -> C
      A -> D
    
    }
    
    mutation {
    
        # Nodes
    
        n1: createUser(username: "new user", password: "secret"){
            uid
            username
        }
    
        n2: updateGroup(gid: "group id"){
            gid
            name
        }
    
        # Edges
    
        addUserToGroup(user: "n1", group: "n2"){
            status
        }
    }
    
    按照这种模式,问题中的graphql突变可能如下所示:

    digraph D {
    
      /* Nodes */
      A 
      B
      C
    
      /* Edges */
    
      A -> B
      A -> C
      A -> D
    
    }
    
    mutation {
    
        # Nodes
    
        n1: createUser(username: "new user", password: "secret"){
            uid
            username
        }
    
        n2: updateGroup(gid: "group id"){
            gid
            name
        }
    
        # Edges
    
        addUserToGroup(user: "n1", group: "n2"){
            status
        }
    }
    
    “边缘操作”
    addUserToGroup
    的输入将是变异查询中先前节点的别名

    这还允许使用权限检查来修饰边缘操作(创建关系的权限可能与每个对象上的权限不同)

    我们完全可以解决这样的问题。不太确定的是,后端框架(尤其是Graphene python)是否提供了允许实现
    addUserToGroup
    (在解析上下文中具有先前的变异结果)的机制。我正在考虑在石墨烯上下文中注入先前结果的
    dict
    。如果成功,我将尝试用技术细节完成答案

    也许已经有办法做到这一点了,我也会去寻找,如果找到答案,我会完成

    如果发现上面的模式不可能,或者发现了不好的做法,我想我会坚持使用两种不同的突变


    编辑1:共享结果 我测试了一种解决上述查询的方法,使用一个和一个基类来处理共享结果。我创建了一个测试

    中间件非常简单,向解析器添加了dict as
    kwarg
    参数:

    class ShareResultMiddleware:
    共享_结果={}
    def解析(self、next、root、info、**args):
    返回下一步(根目录、信息、共享结果=self.shared结果,**参数)
    
    基类也非常简单,可以管理结果在字典中的插入:

    class SharedResult突变(石墨烯突变):
    @类方法
    def mutate(cls,root:None,info:graphene.ResolveInfo,共享结果:dict,*args,**kwargs):
    结果=cls.mutate_和共享_结果(root、info、*args、**kwargs)
    如果root为None:
    节点=信息路径[0]
    共享结果[节点]=结果
    返回结果
    @静力学方法
    def突变和共享结果(*,**):
    返回SharedResult()#覆盖
    
    需要符合共享结果模式的节点状突变将继承自
    SharedResult突变
    ,而不是
    突变
    ,并覆盖
    突变和共享结果
    ,而不是
    突变

    class UpsertParent(SharedResult,ParentType):
    类参数:
    数据=父输入()
    @静力学方法
    def mutate_和_share_结果(根:无,信息:graphene.ResolveInfo,数据:ParentInput,*\uuuuuu,***\uuu):
    返回UpsertParent(id=1,name=“test”)#“UpsertBook”:
    return UpsertBook(**data)#因此,我创建了用于处理并允许在同一查询中引用边状突变中节点状突变的结果。我将只粘贴下面的用法部分:

    5个步骤(有关可执行示例,请参阅)

  • 安装软件包(需要)
  • pip安装石墨烯链突变
    
  • 通过继承
    ShareResult
    之前的