Python 在Django Graphene中继中启用基于PK的过滤,同时保留全局ID 问题
我在GraphQL服务器上使用django graphene和继电器。该实现在Python 在Django Graphene中继中启用基于PK的过滤,同时保留全局ID 问题,python,django,graphql,relay,graphene-python,Python,Django,Graphql,Relay,Graphene Python,我在GraphQL服务器上使用django graphene和继电器。该实现在graphene.relay.Node类中引入了一个覆盖并隐藏Django的ID字段的属性 因此,我可以这样查询: { allBatches(id:"QmF0Y2hOb2RlOjE=") { edges { node { id pk } } } } class PKMixin(object): pk = graphene.Fi
graphene.relay.Node
类中引入了一个覆盖并隐藏Django的ID字段的属性
因此,我可以这样查询:
{
allBatches(id:"QmF0Y2hOb2RlOjE=") {
edges {
node {
id
pk
}
}
}
}
class PKMixin(object):
pk = graphene.Field(type=graphene.Int, source='pk')
得到这样的回答:
{
"data": {
"allBatches": {
"edges": [
{
"node": {
"id": "QmF0Y2hOb2RlOjE=",
"pk": 1
}
}
]
}
}
}
但是,我失去的是通过对象本身的原始ID(或PK)字段进行过滤的能力:
{
allBatches(id:1) {
edges {
node {
id
pk
}
}
}
}
事实上,I无法通过ID过滤对象。
我可以想出两种可能的解决办法:
1.防止django graphene中继劫持和隐藏id
字段,可能会强制它使用不同的字段名,如gid
2.找到一种方法,将pk
作为一个特殊字段包括在内,该字段既可以作为属性,也可以作为筛选器使用
解决方案1
我在1上没有取得任何进展,因为它似乎是django graphene
(可能还有中继标准)规定了一个限制,该字段被称为id
。我看到,id
在多个地方被用作一个神奇的字符串,而且似乎没有一种标准的方法来更改字段名
解决方案2
在2上,我可以让属性使用Mixin
,如下所示:
{
allBatches(id:"QmF0Y2hOb2RlOjE=") {
edges {
node {
id
pk
}
}
}
}
class PKMixin(object):
pk = graphene.Field(type=graphene.Int, source='pk')
但是,我无法通过django filter
使过滤工作,因为FilterSet
没有声明字段pk
,并出现以下错误
“Meta.fields”包含未在此筛选器集上定义的字段:
主键
更新日期: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
要求
我现在陷入困境。我有几个问题:
- 石墨烯django==2.1.0
- django==1.9.12
- django过滤器==1.0.1
- python==2.7.13
batchByPk
字段的内容
最后需要注意的是,目前graphene django的
DjangoFilterConnectionField
没有以有效的方式实现,因此您可能根本不想使用它。我不确定您是否仍然想要答案,但至少让我试着回答您的问题。如果我的理解是错误的,请更正。我只是愿意帮忙
实际上pk
应该是DetailView
而不是ListView
与filter
一起使用
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')
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,)
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)
nodes.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')
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,)
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)
querys.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')
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,)
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
中留下注释,以示类推。使用我的黑客解决方案失眠
应用程序将用未知参数pk…
警告我。但有效
查询
query{
# objection(id: "T2JqZWN0aW9uTm9kZTo1"){
# id
# report
# hidden
# }
allObjections(pk: 5){
edges{
node{
id
pk
hidden
report
}
}
}
}
响应
{
"data": {
"allObjections": {
"edges": [
{
"node": {
"id": "T2JqZWN0aW9uTm9kZTo1",
"pk": 5,
"hidden": false,
"report": false
}
}
]
}
}
}
API总是返回带有派生全局ID的节点。作为客户端,如果需要查找节点,我可以使用相同的标识符。公开底层PK似乎没有必要,除非A)您正在与某个其他服务交互,该服务使用PK作为参考,或者B)从业务规则的角度来看,PK对客户端来说是重要的(即,客户端比较PK值以实施某些业务逻辑)。为什么您觉得公开PK是必要的?基本上,与PKS紧密耦合的遗留代码A)和B)对我们来说都是正确的。中带有ID的网站URL是合法的用例。我考虑过使用graphene,但是这是一个很强的限制,我可能需要离开这个想法,你能告诉我更多关于
DjangoFilterConnectionField
的效率吗?Github上是否有文档或问题,我可以阅读以了解更多信息?source='id'
没有帮助-相同的错误:>'Meta.fields'包含未在此过滤器集上定义的字段:pk此错误持续存在,除非此时使用pk
@rtindru声明显式过滤器集,Graphene中的所有连接代码都实现了限制/偏移分页,而不是真正的基于光标的分页,因此它没有该模式的任何真正好处——只是假装而已。