【问题标题】:Enable PK based filtering in Django Graphene Relay while retaining Global IDs在 Django Graphene Relay 中启用基于 PK 的过滤,同时保留全局 ID
【发布时间】:2019-06-17 03:04:41
【问题描述】:

问题

我在我们的 GraphQL 服务器上使用 django-graphene 和 Relay。该实现在graphene.relay.Node 类中强加了一个Global ID requirement,该类覆盖并隐藏了Django 的ID 字段

结果,我可以这样查询:

{
    allBatches(id:"QmF0Y2hOb2RlOjE=") {
    edges {
      node {
        id
        pk
      }
    }
  }
}

得到这个响应:

{
  "data": {
    "allBatches": {
      "edges": [
        {
          "node": {
            "id": "QmF0Y2hOb2RlOjE=",
            "pk": 1
          }
        }
      ]
    }
  }
}

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

{
    allBatches(id:1) {
    edges {
      node {
        id
        pk
      }
    }
  }
}

事实上,我根本无法通过 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')

但是,我无法通过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)

现在我可以让 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"
    }
  }
}

但是我非常担心这会破坏下游的某些东西, 也许在缓存级别? 此外,这仍然不允许按 ID 过滤,因为过滤取决于 DjangoFilterConnectionField

请求

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

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

Github 上的相关问题

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

版本

  • 石墨烯-django==2.1.0
  • django==1.9.12
  • django-filter==1.0.1
  • python==2.7.13

【问题讨论】:

  • API 始终返回具有派生全局 ID 的节点。作为客户端,如果我需要查找节点,我可以使用相同的标识符。暴露底层 PK 似乎没有必要,除非 A)您正在与之交互的其他一些服务使用 PK 作为参考或 B)从业务规则的角度来看,PK 对客户端很重要(即客户端将 PK 值与执行一些业务逻辑)。为什么觉得有必要公开PK?
  • 与 PK 紧密耦合的遗留代码
  • 基本上,A) 和 B) 对我们来说都是正确的。
  • 带有 ID 的网站 URL 是合法的用例。
  • 我考虑过使用石墨烯,但这是一个很大的限制,我可能需要放弃这个想法

标签: python django graphql relay graphene-python


【解决方案1】:

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

class PKMixin(object):
    pk = graphene.Field(type=graphene.Int, source='id')

另外,如果您只想获取一条记录,则无论如何都不应该通过连接字段。您应该在架构上定义类似 batchByPk 的字段。

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

【讨论】:

  • 你能告诉我更多关于DjangoFilterConnectionField是如何低效的吗?我可以阅读 Github 上的文档或问题以了解更多信息吗?
  • source='id' 没有帮助 - 同样的错误:>'Meta.fields' 包含未在此 FilterSet 上定义的字段:pk 此错误持续存在,除非使用 pk 声明显式 FilterSet。
  • @rtindru 目前,Graphene 中的所有连接代码都实现了限制/偏移分页,而不是真正的基于光标的分页,因此它没有任何模式的真正好处——这只是假的。
【解决方案2】:

我不确定您是否仍然想要答案,但至少让我尝试回答您的问题。如果我的理解有误,请纠正。我只是愿意帮忙

实际上pk 应该是DetailView 而不是ListViewfilter 一起使用。

requirements.txt

graphene-django==2.7.1
django==3.0.1
django-filter==2.2.0
python==3.8.1

models.py

from django.contrib.auth import get_user_model
from django.db import models

User = get_user_model()


class Objection(models.Model):
    detail = models.TextField(null=True, blank=True)
    hidden = models.BooleanField(default=False)
    report = models.BooleanField(default=False)
    created_by = models.ForeignKey(User, on_delete=models.CASCADE, related_name='objections',
                                   related_query_name='objection')

nodes.py

import django_filters
import graphene
from graphene import relay
from graphene_django import DjangoObjectType

from multy_herr.objections.models import Objection


class ObjectionFilter(django_filters.FilterSet):
    pk = django_filters.NumberFilter(field_name='pk')

    class Meta:
        model = Objection
        fields = [
            'pk',
        ]


class ObjectionNode(DjangoObjectType):
    pk = graphene.Field(type=graphene.Int, source='id')

    class Meta:
        model = Objection
        fields = [
            'id',
            'pk',
            'detail',
            'hidden',
            'report',
        ]
        filter_fields = {
            'pk': ['exact'],
            'detail': ['icontains', 'istartswith'],
            'created_by__name': ['icontains', ],
            'hidden': ['exact'],
            'report': ['exact'],
        }
        interfaces = (relay.Node,)


queries.py

import graphene
from graphene import relay
from graphene_django.filter import DjangoFilterConnectionField

from multy_herr.objections.grapheql.nodes import ObjectionNode, ObjectionFilter
from multy_herr.objections.models import Objection


class ObjectionQuery(graphene.ObjectType):
    objection = relay.Node.Field(ObjectionNode)
    all_objections = DjangoFilterConnectionField(ObjectionNode,
                                                 filterset_class=ObjectionFilter)

    def resolve_all_objections(self, info, **kwargs):
        if info.context.user.is_authenticated is False:
            return Objection.objects.none()
        return Objection.objects.filter(created_by=info.context.user)

为了类比,我在query 留下评论。使用我的 hackish 解决方案 Insomnia 应用程序会用 Unknown argument pk ... 警告我。但有效

query

query{
#   objection(id: "T2JqZWN0aW9uTm9kZTo1"){
#     id
#     report
#     hidden
#   }
  allObjections(pk: 5){
    edges{
      node{
        id
        pk
        hidden
        report
      }
    }
  }
}

response

{
  "data": {
    "allObjections": {
      "edges": [
        {
          "node": {
            "id": "T2JqZWN0aW9uTm9kZTo1",
            "pk": 5,
            "hidden": false,
            "report": false
          }
        }
      ]
    }
  }
}

【讨论】:

    猜你喜欢
    • 2019-12-20
    • 2021-07-04
    • 2019-12-18
    • 2020-05-19
    • 2021-11-09
    • 2019-05-02
    • 2017-03-15
    • 2016-11-23
    • 2023-03-30
    相关资源
    最近更新 更多