[英]How to filter a query by a list of ids in GraphQL using graphene-django?
I'm trying to perform a GraphQL query using Django and Graphene.我正在尝试使用 Django 和 Graphene 执行 GraphQL 查询。 To query one single object using the id I did the following:
要使用 id 查询单个对象,我执行了以下操作:
{
samples(id:"U2FtcGxlU2V0VHlwZToxMjYw") {
edges {
nodes {
name
}
}
}
}
And it just works fine.它工作正常。 Problem arise when I try to query with more than one id, like the following:
当我尝试使用多个 id 进行查询时出现问题,如下所示:
{
samples(id_In:"U2FtcGxlU2V0VHlwZToxMjYw, U2FtcGxlU2V0VHlwZToxMjYx") {
edges {
nodes {
name
}
}
}
}
In the latter case I got the following error:在后一种情况下,我收到以下错误:
argument should be a bytes-like object or ASCII string, not 'list'
And this is a sketch of how defined the Type and Query in django-graphene
这是如何在
django-graphene
定义类型和查询的草图
class SampleType(DjangoObjectType):
class Meta:
model = Sample
filter_fields = {
'id': ['exact', 'in'],
}
interfaces = (graphene.relay.Node,)
class Query(object):
samples = DjangoFilterConnectionField(SampleType)
def resolve_sample_sets(self, info, **kwargs):
return Sample.objects.all()
GlobalIDMultipleChoiceFilter
from django-graphene kinda solves this issue, if you put "in" in the field name.来自 django-graphene 的
GlobalIDMultipleChoiceFilter
有点解决这个问题,如果你在字段名称中输入“in”。 You can create filters like您可以创建过滤器,例如
from django_filters import FilterSet
from graphene_django.filter import GlobalIDMultipleChoiceFilter
class BookFilter(FilterSet):
author = GlobalIDMultipleChoiceFilter()
and use it by并使用它
{
books(author: ["<GlobalID1>", "<GlobalID2>"]) {
edges {
nodes {
name
}
}
}
}
Still not perfect, but the need for custom code is minimized.仍然不完美,但对自定义代码的需求已最小化。
I had trouble implementing the 'in' filter as well--it appears to be misimplemented in graphene-django right now and does not work as expected.我在实现“in”过滤器时也遇到了麻烦——它现在似乎在 graphene-django 中被错误实现,并且没有按预期工作。 Here are the steps to make it work:
以下是使其工作的步骤:
from base64 import b64decode
def get_pk_from_node_id(node_id: str):
"""Gets pk from node_id"""
model_with_pk = b64decode(node_id).decode('utf-8')
model_name, pk = model_with_pk.split(":")
return pk
class SampleType(DjangoObjectType):
class Meta:
model = Sample
filter_fields = {
'id': ['exact'],
}
interfaces = (graphene.relay.Node,)
class Query(object):
samples = DjangoFilterConnectionField(SampleType, id__in=graphene.List(graphene.ID))
def resolve_samples(self, info, **kwargs):
# filter_field for 'in' seems to not work, this hack works
id__in = kwargs.get('id__in')
if id__in:
node_ids = kwargs.pop('id__in')
pk_list = [get_pk_from_node_id(node_id) for node_id in node_ids]
return Sample._default_manager.filter(id__in=pk_list)
return Sample._default_manager.all()
This will allow you to call the filter with the following api.这将允许您使用以下 api 调用过滤器。 Note the use of an actual array in the signature (I think this is a better API than sending a comma separated string of values).
注意签名中实际数组的使用(我认为这是一个比发送逗号分隔的值字符串更好的 API)。 This solution still allows you to add other filters to the request and they will chain together correctly.
此解决方案仍然允许您向请求添加其他过滤器,它们将正确链接在一起。
{
samples(id_In: ["U2FtcGxlU2V0VHlwZToxMjYw", "U2FtcGxlU2V0VHlwZToxMjYx"]) {
edges {
nodes {
name
}
}
}
}
You can easily use a Filter just put this with your nodes.您可以轻松地使用过滤器,只需将其与您的节点放在一起即可。
class ReportFileFilter(FilterSet):
id = GlobalIDMultipleChoiceFilter()
Then in your query just use -然后在您的查询中使用 -
class Query(graphene.ObjectType):
all_report_files = DjangoFilterConnectionField(ReportFileNode, filterset_class=ReportFileFilter)
This is for relay implementation of graphql django.这是用于 graphql django 的中继实现。
None of the existing answers seemed to work for me as they were presented, however with some slight changes I managed to resolve my problem as follows:现有的答案似乎都不适合我,但是通过一些细微的更改,我设法解决了我的问题,如下所示:
You can create a custom FilterSet
class for your object type, and filter the field by using the GlobalIDMultipleChoiceFilter
.您可以为您的对象类型创建自定义
FilterSet
类,并使用GlobalIDMultipleChoiceFilter
过滤字段。 for example:例如:
from django_filters import FilterSet
from graphene_django.filter import GlobalIDFilter, GlobalIDMultipleChoiceFilter
class SampleFilter(FilterSet):
id = GlobalIDFilter()
id__in = GlobalIDMultipleChoiceFilter(field_name="id")
class Meta:
model = Sample
fields = (
"id_in",
"id",
)
Something I came cross is that you can not have filter_fields defined with this approach .我遇到的问题是您不能使用这种方法定义 filter_fields 。 Instead, you have to only rely on the custom
FilterSet
class exclusively, making your object type effectively look like this:相反,您只需要专门依赖自定义
FilterSet
类,使您的对象类型有效地如下所示:
from graphene import relay
from graphene_django import DjangoObjectType
class SampleType(DjangoObjectType):
class Meta:
model = Sample
filterset_class = SampleFilter
interfaces = (relay.Node,)
Another way is to tell the Relay filter of graphene_django to also deals with a list.另一种方法是告诉 graphene_django 的 Relay 过滤器也处理一个列表。 This filter is register in a mixin in graphene_django and applied to any filter you define.
该过滤器注册在 graphene_django 的 mixin 中,并应用于您定义的任何过滤器。
So here my solution:所以这里我的解决方案:
from graphene_django.filter.filterset import (
GlobalIDFilter,
GrapheneFilterSetMixin,
)
from graphql_relay import from_global_id
class CustomGlobalIDFilter(GlobalIDFilter):
"""Allow __in lookup for IDs"""
def filter(self, qs, value):
if isinstance(value, list):
value_lst = [from_global_id(v)[1] for v in value]
return super(GlobalIDFilter, self).filter(qs, value_lst)
else:
return super().filter(qs, value)
# Fix the mixin defaults
GrapheneFilterSetMixin.FILTER_DEFAULTS.update({
AutoField: {"filter_class": CustomGlobalIDFilter},
OneToOneField: {"filter_class": CustomGlobalIDFilter},
ForeignKey: {"filter_class": CustomGlobalIDFilter},
})
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.