在Django Graphene Relay中启用基于PK的过滤,同时保留全局ID

rti*_*dru 9 python django relay graphql graphene-python

问题

我在我们的GraphQL服务器上将django-graphene与Relay一起使用。该实现在类中强加了全局ID要求,该要求覆盖并隐藏Django的ID字段。graphene.relay.Node

结果,我可以这样查询:

{
    allBatches(id:"QmF0Y2hOb2RlOjE=") {
    edges {
      node {
        id
        pk
      }
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

并获得以下响应:

{
  "data": {
    "allBatches": {
      "edges": [
        {
          "node": {
            "id": "QmF0Y2hOb2RlOjE=",
            "pk": 1
          }
        }
      ]
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

但是,我失去的是能够按对象本身的原始ID(或PK)字段进行过滤的功能:

{
    allBatches(id:1) {
    edges {
      node {
        id
        pk
      }
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

实际上,我根本无法通过ID过滤对象。我可以想到两种可能的解决方法:1.防止django-graphene-relay劫持和遮蔽该id字段,也许强迫它使用其他字段名称,例如gid 2.查找一种将其包括pk为特殊字段的方法可作为属性和过滤器使用

解决方案1

我在1上没有取得任何进展,因为看起来django-graphene(似乎是中继标准)强加了对该字段的限制id。我看到它id已在多个地方用作魔术字符串,并且似乎没有改变字段名称的标准方法。

解决方案2

在2上,我可以使该属性Mixin像这样工作:

class PKMixin(object):
    pk = graphene.Field(type=graphene.Int, source='pk')
Run Code Online (Sandbox Code Playgroud)

但是,我无法通过过滤进行django-filter工作,因为的FilterSet没有pk声明字段,并因以下错误而中断

'Meta.fields'包含未在此FilterSet上定义的字段:pk

更新2

我尝试了以下方法:

class PKFilteringNode(Node):

    @classmethod
    def get_node_from_global_id(cls, info, global_id, only_type=None):
        # So long as only_type is set; if we detect that the global_id is a pk and not a global ID;
        # then coerce it to be a proper global ID before fetching
        if only_type:
            try:
                int(global_id)
                global_id = cls.to_global_id(only_type._meta.name, global_id)
                return super(PKFilteringNode, cls).get_node_from_global_id(info, global_id, only_type)
            except ValueError:
                pass
        return super(PKFilteringNode, cls).get_node_from_global_id(info, global_id, only_type)
Run Code Online (Sandbox Code Playgroud)

现在我可以让GraphQL做到这一点:

{
  batchA: batch(id: "QmF0Y2hOb2RlOjE=") {
    id
    name
  }
  batchB: batch(id: 1) {
    id
    name
  }
}
{
  "data": {
    "batchA": {
      "id": "QmF0Y2hOb2RlOjE=",
      "name": "Default Batch"
    },
    "batchB": {
      "id": "QmF0Y2hOb2RlOjE=",
      "name": "Default Batch"
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

但是我非常担心这会破坏某些下游的缓存水平?同样,这仍然不允许通过ID进行过滤,因为过滤取决于 DjangoFilterConnectionField

请求

我现在被困住了。我有几个问题:

  1. 这是一个不寻常的要求吗?当我希望保留按pk过滤的功能时,我问的是错误的问题吗?
  2. 是否有解决此问题的标准模式?

Github相关问题

https://github.com/graphql-python/graphene-django/issues/349

版本号

  • graphene-django == 2.1.0
  • django == 1.9.12
  • django-filter == 1.0.1
  • python == 2.7.13

And*_*ram 0

您是否尝试过解决方案 2 但使用 id 作为源?

class PKMixin(object):
    pk = graphene.Field(type=graphene.Int, source='id')
Run Code Online (Sandbox Code Playgroud)

另外,如果您只想获取单个记录,则无论如何都不应该通过连接字段。您应该batchByPk在模式上定义类似字段的内容。

最后要注意的是,目前 graphene-django 还DjangoFilterConnectionField没有以有效的方式实现,所以你可能根本不想使用它。

  • @rtindru 目前,Graphene 中的所有连接代码都实现了限制/偏移分页,而不是真正的基于光标的分页,因此它没有该模式的任何真正好处——它只是伪造的。 (3认同)