Django 石墨烯继电器 order_by (OrderingFilter)
Django Graphene Relay order_by (OrderingFilter)
我有一个带有中继和过滤器的石墨烯界面。它工作得很好,但我想添加 order_by 选项。我的对象看起来像:
class FooGQLType(DjangoObjectType):
class Meta:
model = Foo
exclude_fields = ('internal_id',)
interfaces = (graphene.relay.Node,)
filter_fields = {
"id": ["exact"],
"code": ["exact", "icontains"],
}
connection_class = ExtendedConnection
class Query(graphene.ObjectType):
foo = DjangoFilterConnectionField(FooGQLType)
ExtendedConnection 应该不相关但是:
class ExtendedConnection(graphene.Connection):
class Meta:
abstract = True
total_count = graphene.Int()
def resolve_total_count(root, info, **kwargs):
return root.length
这让我可以像 foo(code_Icontains:"bar")
一样查询。
根据 the Graphene documentation,我应该为此使用 FilterSet 中的 OrderingFilter。我觉得有点烦人,因为过滤器应该是自动的,但如果我这样做:
class FooGQLFilter(FilterSet):
class Meta:
model = Foo
order_by = OrderingFilter(
fields=(
('code', 'code'),
('lastName', 'last_name'),
('otherNames', 'other_names'),
)
)
我收到一个错误,我需要提供 fields
或 exclude
:
AssertionError: Setting 'Meta.model' without either 'Meta.fields' or 'Meta.exclude' has been deprecated since 0.15.0 and is now disallowed. Add an explicit 'Meta.fields' or 'Meta.exclude' to the FooGQLFilter class.
因此,如果我添加一个 fields = []
使其静音,它就会编译。
但是,当我在以下位置使用它时:
foo = DjangoFilterConnectionField(FooGQLType, filterset_class=FooGQLFilter)
我的 code_Icontains
等常规过滤器消失了。我可以在那里再次添加它们,但这很愚蠢。快速查看源代码,似乎 Relay 或 django-filters 已经创建了一个 FilterSet class(有意义)并且以这种方式覆盖它显然是一个糟糕的主意。
如何在我的 Graphene Relay 过滤对象上添加 orderBy 过滤器?我觉得这应该很简单,但我正在努力解决这个问题。
我也看到了 subclassing DjangoFilterConnectionField
的例子,其中 connection_resolver
以某种方式注入了 order_by,但这告诉我没有 orderBy 参数。
此解决方案仅适用于 2.6.0 之前的 django-graphene,请参阅 Alok Ramteke 的解决方案以获取最新版本
我已经从 a GitHub issue 中改编了关于这个主题的解决方案:
from graphene_django.filter import DjangoFilterConnectionField
from graphene.utils.str_converters import to_snake_case
class OrderedDjangoFilterConnectionField(DjangoFilterConnectionField):
"""
Adapted from https://github.com/graphql-python/graphene/issues/251
Substituting:
`claims = DjangoFilterConnectionField(ClaimsGraphQLType)`
with:
```
claims = OrderedDjangoFilterConnectionField(ClaimsGraphQLType,
orderBy=graphene.List(of_type=graphene.String))
```
"""
@classmethod
def connection_resolver(cls, resolver, connection, default_manager, max_limit,
enforce_first_or_last, filterset_class, filtering_args,
root, info, **args):
filter_kwargs = {k: v for k, v in args.items() if k in filtering_args}
qs = filterset_class(
data=filter_kwargs,
queryset=default_manager.get_queryset(),
request=info.context
).qs
order = args.get('orderBy', None)
if order:
if type(order) is str:
snake_order = to_snake_case(order)
else:
snake_order = [to_snake_case(o) for o in order]
qs = qs.order_by(*snake_order)
return super(DjangoFilterConnectionField, cls).connection_resolver(
resolver,
connection,
qs,
max_limit,
enforce_first_or_last,
root,
info,
**args
)
要使用它,只需修改来自:
的查询
claims = DjangoFilterConnectionField(ClaimsGraphQLType)
到
claims = OrderedDjangoFilterConnectionField(ClaimsGraphQLType,
orderBy=graphene.List(of_type=graphene.String))
然后您可以查询:
{ claims(status: 2, orderBy: "-id") { id } }
或
{ claims(status: 2, orderBy: ["creationDate", "lastName"]) { id } }
Eric 的解决方案不适用于当前的 graphene-django 版本 (2.9.1) 或高于 graphene-django 2.6.0 的版本。
DjangoFilterConnectionField 方法在 2.7.0 版本中发生了变化。
有关详细信息,您可以查看更新日志 here
使用Eric的解决方案,会产生错误,
connection_resolver() missing 1 required positional argument: 'info’
所以我修改了解决方案并且效果很好。
from graphene_django.filter import DjangoFilterConnectionField
from graphene.utils.str_converters import to_snake_case
class OrderedDjangoFilterConnectionField(DjangoFilterConnectionField):
@classmethod
def resolve_queryset(
cls, connection, iterable, info, args, filtering_args, filterset_class
):
qs = super(DjangoFilterConnectionField, cls).resolve_queryset(
connection, iterable, info, args
)
filter_kwargs = {k: v for k, v in args.items() if k in filtering_args}
qs = filterset_class(data=filter_kwargs, queryset=qs, request=info.context).qs
order = args.get('orderBy', None)
if order:
if type(order) is str:
snake_order = to_snake_case(order)
else:
snake_order = [to_snake_case(o) for o in order]
qs = qs.order_by(*snake_order)
return qs
嘿,我相信我有一个简单易行的答案来处理 orderBy,尤其是作为石墨烯中的列表。我回答的原因不是因为我相信我有最聪明的解决方案,我只是想知道如果我在不知不觉中造成伤害有什么区别。我实际上不理解 graphene_django 中的连接内容,所以我采取了不同的路线,灵感来自于观看有关在 GraphQL 中排序对象的讨论。
https://www.youtube.com/watch?v=dDxUu-K2qdE
所以首先使用你的 FooType
我将字段添加为枚举
class FooGQLType(DjangoObjectType):
class Meta:
model = Foo
class FooFields(graphene.Enum):
CODE = "code"
LAST_NAME = "last_name"
OTHER_NAMES = "other_names"
class Directions(graphene.Enum):
ASC = "asc"
DESC = "desc"
然后输入以在 Query
中传递它们
class FooOrderByInput(graphene.InputObjectType):
order_by = FooFields()
direction = Directions()
我不确定您要过滤的内容,但由于问题主要是关于 order_by
,我暂时假设它是一个随机字符串。我觉得有一种方法可以发出这一行数据库请求,但我不确定如何。
ORDER_BY = 'order_by'
DIRECTION = 'direction'
class Query(graphene.ObjectType):
foo = graphene.Field(FooGQLType, input=graphene.List(FooOrderByInput), filter_opt=graphene.String())
def resolve_foo(self, info, input):
qs = Foo.objects.filter(filter_opt=filter_opt)
for obj in input:
order_by = obj.get(ORDER_BY)
direction = "-" if obj.get(DIRECTION) == Directions.DESC else ""
qs = qs.order_by(f"{direction}{order_by}")
return qs
关于这个的最好的事情(无论如何对我来说)是它允许查询多个字段,比如
query ($input: [FooOrderByInput]) {
foo (input: $input) {
id
code
lastName
otherNames
}
}
VARIABLES
{
"input": [
{
"orderBy": "LAST_NAME",
"direction": "DESC"
},
{
"orderBy": "CODE",
"direction": "ASC"
}
]
}
而且除了那些拼写正确的关键字之外,它也不允许任何其他内容。它也不必将 camelCase 处理为 snake_case.
如果有更好的解决方案或者我的解决方案很糟糕,请告诉我。我很高兴这似乎已经解决了!
我有一个带有中继和过滤器的石墨烯界面。它工作得很好,但我想添加 order_by 选项。我的对象看起来像:
class FooGQLType(DjangoObjectType):
class Meta:
model = Foo
exclude_fields = ('internal_id',)
interfaces = (graphene.relay.Node,)
filter_fields = {
"id": ["exact"],
"code": ["exact", "icontains"],
}
connection_class = ExtendedConnection
class Query(graphene.ObjectType):
foo = DjangoFilterConnectionField(FooGQLType)
ExtendedConnection 应该不相关但是:
class ExtendedConnection(graphene.Connection):
class Meta:
abstract = True
total_count = graphene.Int()
def resolve_total_count(root, info, **kwargs):
return root.length
这让我可以像 foo(code_Icontains:"bar")
一样查询。
根据 the Graphene documentation,我应该为此使用 FilterSet 中的 OrderingFilter。我觉得有点烦人,因为过滤器应该是自动的,但如果我这样做:
class FooGQLFilter(FilterSet):
class Meta:
model = Foo
order_by = OrderingFilter(
fields=(
('code', 'code'),
('lastName', 'last_name'),
('otherNames', 'other_names'),
)
)
我收到一个错误,我需要提供 fields
或 exclude
:
AssertionError: Setting 'Meta.model' without either 'Meta.fields' or 'Meta.exclude' has been deprecated since 0.15.0 and is now disallowed. Add an explicit 'Meta.fields' or 'Meta.exclude' to the FooGQLFilter class.
因此,如果我添加一个 fields = []
使其静音,它就会编译。
但是,当我在以下位置使用它时:
foo = DjangoFilterConnectionField(FooGQLType, filterset_class=FooGQLFilter)
我的 code_Icontains
等常规过滤器消失了。我可以在那里再次添加它们,但这很愚蠢。快速查看源代码,似乎 Relay 或 django-filters 已经创建了一个 FilterSet class(有意义)并且以这种方式覆盖它显然是一个糟糕的主意。
如何在我的 Graphene Relay 过滤对象上添加 orderBy 过滤器?我觉得这应该很简单,但我正在努力解决这个问题。
我也看到了 subclassing DjangoFilterConnectionField
的例子,其中 connection_resolver
以某种方式注入了 order_by,但这告诉我没有 orderBy 参数。
此解决方案仅适用于 2.6.0 之前的 django-graphene,请参阅 Alok Ramteke 的解决方案以获取最新版本
我已经从 a GitHub issue 中改编了关于这个主题的解决方案:
from graphene_django.filter import DjangoFilterConnectionField
from graphene.utils.str_converters import to_snake_case
class OrderedDjangoFilterConnectionField(DjangoFilterConnectionField):
"""
Adapted from https://github.com/graphql-python/graphene/issues/251
Substituting:
`claims = DjangoFilterConnectionField(ClaimsGraphQLType)`
with:
```
claims = OrderedDjangoFilterConnectionField(ClaimsGraphQLType,
orderBy=graphene.List(of_type=graphene.String))
```
"""
@classmethod
def connection_resolver(cls, resolver, connection, default_manager, max_limit,
enforce_first_or_last, filterset_class, filtering_args,
root, info, **args):
filter_kwargs = {k: v for k, v in args.items() if k in filtering_args}
qs = filterset_class(
data=filter_kwargs,
queryset=default_manager.get_queryset(),
request=info.context
).qs
order = args.get('orderBy', None)
if order:
if type(order) is str:
snake_order = to_snake_case(order)
else:
snake_order = [to_snake_case(o) for o in order]
qs = qs.order_by(*snake_order)
return super(DjangoFilterConnectionField, cls).connection_resolver(
resolver,
connection,
qs,
max_limit,
enforce_first_or_last,
root,
info,
**args
)
要使用它,只需修改来自:
的查询claims = DjangoFilterConnectionField(ClaimsGraphQLType)
到
claims = OrderedDjangoFilterConnectionField(ClaimsGraphQLType,
orderBy=graphene.List(of_type=graphene.String))
然后您可以查询:
{ claims(status: 2, orderBy: "-id") { id } }
或
{ claims(status: 2, orderBy: ["creationDate", "lastName"]) { id } }
Eric 的解决方案不适用于当前的 graphene-django 版本 (2.9.1) 或高于 graphene-django 2.6.0 的版本。
DjangoFilterConnectionField 方法在 2.7.0 版本中发生了变化。 有关详细信息,您可以查看更新日志 here
使用Eric的解决方案,会产生错误,
connection_resolver() missing 1 required positional argument: 'info’
所以我修改了解决方案并且效果很好。
from graphene_django.filter import DjangoFilterConnectionField
from graphene.utils.str_converters import to_snake_case
class OrderedDjangoFilterConnectionField(DjangoFilterConnectionField):
@classmethod
def resolve_queryset(
cls, connection, iterable, info, args, filtering_args, filterset_class
):
qs = super(DjangoFilterConnectionField, cls).resolve_queryset(
connection, iterable, info, args
)
filter_kwargs = {k: v for k, v in args.items() if k in filtering_args}
qs = filterset_class(data=filter_kwargs, queryset=qs, request=info.context).qs
order = args.get('orderBy', None)
if order:
if type(order) is str:
snake_order = to_snake_case(order)
else:
snake_order = [to_snake_case(o) for o in order]
qs = qs.order_by(*snake_order)
return qs
嘿,我相信我有一个简单易行的答案来处理 orderBy,尤其是作为石墨烯中的列表。我回答的原因不是因为我相信我有最聪明的解决方案,我只是想知道如果我在不知不觉中造成伤害有什么区别。我实际上不理解 graphene_django 中的连接内容,所以我采取了不同的路线,灵感来自于观看有关在 GraphQL 中排序对象的讨论。
https://www.youtube.com/watch?v=dDxUu-K2qdE
所以首先使用你的 FooType
我将字段添加为枚举
class FooGQLType(DjangoObjectType):
class Meta:
model = Foo
class FooFields(graphene.Enum):
CODE = "code"
LAST_NAME = "last_name"
OTHER_NAMES = "other_names"
class Directions(graphene.Enum):
ASC = "asc"
DESC = "desc"
然后输入以在 Query
class FooOrderByInput(graphene.InputObjectType):
order_by = FooFields()
direction = Directions()
我不确定您要过滤的内容,但由于问题主要是关于 order_by
,我暂时假设它是一个随机字符串。我觉得有一种方法可以发出这一行数据库请求,但我不确定如何。
ORDER_BY = 'order_by'
DIRECTION = 'direction'
class Query(graphene.ObjectType):
foo = graphene.Field(FooGQLType, input=graphene.List(FooOrderByInput), filter_opt=graphene.String())
def resolve_foo(self, info, input):
qs = Foo.objects.filter(filter_opt=filter_opt)
for obj in input:
order_by = obj.get(ORDER_BY)
direction = "-" if obj.get(DIRECTION) == Directions.DESC else ""
qs = qs.order_by(f"{direction}{order_by}")
return qs
关于这个的最好的事情(无论如何对我来说)是它允许查询多个字段,比如
query ($input: [FooOrderByInput]) {
foo (input: $input) {
id
code
lastName
otherNames
}
}
VARIABLES
{
"input": [
{
"orderBy": "LAST_NAME",
"direction": "DESC"
},
{
"orderBy": "CODE",
"direction": "ASC"
}
]
}
而且除了那些拼写正确的关键字之外,它也不允许任何其他内容。它也不必将 camelCase 处理为 snake_case.
如果有更好的解决方案或者我的解决方案很糟糕,请告诉我。我很高兴这似乎已经解决了!