mirror of
https://github.com/graphql-python/graphene-django.git
synced 2025-07-13 17:52:19 +03:00
added filtering and parent node support
This commit is contained in:
parent
fca7fa647e
commit
15a6744aea
|
@ -97,7 +97,7 @@ class DjangoConnectionField(ConnectionField):
|
|||
return connection
|
||||
|
||||
@classmethod
|
||||
def connection_resolver(
|
||||
def connection_resolver_original(
|
||||
cls,
|
||||
resolver,
|
||||
connection,
|
||||
|
@ -137,6 +137,49 @@ class DjangoConnectionField(ConnectionField):
|
|||
|
||||
return on_resolve(iterable)
|
||||
|
||||
@classmethod
|
||||
def connection_resolver(
|
||||
cls,
|
||||
resolver,
|
||||
connection,
|
||||
default_manager,
|
||||
max_limit,
|
||||
enforce_first_or_last,
|
||||
root,
|
||||
info,
|
||||
**args
|
||||
):
|
||||
_parent = args.get('know_parent', False)
|
||||
|
||||
if not _parent:
|
||||
if hasattr(info.parent_type._meta, 'know_parent_fields'):
|
||||
options = info.parent_type._meta.know_parent_fields
|
||||
assert isinstance(options, (list, tuple)), \
|
||||
"know_parent_fields should be list or tuple"
|
||||
_parent = info.field_name in options
|
||||
|
||||
def new_resolver(root, info, **kwargs):
|
||||
qs = resolver(root, info, **kwargs)
|
||||
if qs is None:
|
||||
qs = default_manager.filter()
|
||||
if _parent and root is not None:
|
||||
instances = []
|
||||
for instance in qs:
|
||||
setattr(instance, '_parent', root)
|
||||
instances.append(instance)
|
||||
return instances
|
||||
return qs
|
||||
|
||||
return cls.connection_resolver_original(
|
||||
new_resolver,
|
||||
connection,
|
||||
default_manager,
|
||||
max_limit,
|
||||
enforce_first_or_last,
|
||||
root,
|
||||
info,
|
||||
**args)
|
||||
|
||||
def get_resolver(self, parent_resolver):
|
||||
return partial(
|
||||
self.connection_resolver,
|
||||
|
|
|
@ -3,7 +3,8 @@ from functools import partial
|
|||
|
||||
from graphene.types.argument import to_arguments
|
||||
from ..fields import DjangoConnectionField
|
||||
from .utils import get_filtering_args_from_filterset, get_filterset_class
|
||||
from .utils import get_filterset_class, get_filtering_args_from_filterset, \
|
||||
make_qs
|
||||
|
||||
|
||||
class DjangoFilterConnectionField(DjangoConnectionField):
|
||||
|
@ -24,18 +25,15 @@ class DjangoFilterConnectionField(DjangoConnectionField):
|
|||
self._base_args = None
|
||||
super(DjangoFilterConnectionField, self).__init__(type, *args, **kwargs)
|
||||
|
||||
@property
|
||||
def args(self):
|
||||
return to_arguments(self._base_args or OrderedDict(), self.filtering_args)
|
||||
|
||||
@args.setter
|
||||
def args(self, args):
|
||||
self._base_args = args
|
||||
|
||||
@property
|
||||
def filterset_class(self):
|
||||
if not self._filterset_class:
|
||||
fields = self._fields or self.node_type._meta.filter_fields
|
||||
if hasattr(self.node_type._meta, 'neomodel_filter_fields'):
|
||||
fields = self.node_type._meta.neomodel_filter_fields
|
||||
elif hasattr(self, '_fields'):
|
||||
fields = self._fields
|
||||
else:
|
||||
fields = []
|
||||
meta = dict(model=self.model, fields=fields)
|
||||
if self._extra_filter_meta:
|
||||
meta.update(self._extra_filter_meta)
|
||||
|
@ -46,6 +44,14 @@ class DjangoFilterConnectionField(DjangoConnectionField):
|
|||
|
||||
return self._filterset_class
|
||||
|
||||
@property
|
||||
def args(self):
|
||||
return to_arguments(self._base_args or OrderedDict(), self.filtering_args)
|
||||
|
||||
@args.setter
|
||||
def args(self, args):
|
||||
self._base_args = args
|
||||
|
||||
@property
|
||||
def filtering_args(self):
|
||||
return get_filtering_args_from_filterset(self.filterset_class, self.node_type)
|
||||
|
@ -74,38 +80,6 @@ class DjangoFilterConnectionField(DjangoConnectionField):
|
|||
queryset.query.set_limits(low, high)
|
||||
return queryset
|
||||
|
||||
@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
|
||||
|
||||
return super(DjangoFilterConnectionField, cls).connection_resolver(
|
||||
resolver,
|
||||
connection,
|
||||
qs,
|
||||
max_limit,
|
||||
enforce_first_or_last,
|
||||
root,
|
||||
info,
|
||||
**args
|
||||
)
|
||||
|
||||
def get_resolver(self, parent_resolver):
|
||||
return partial(
|
||||
self.connection_resolver,
|
||||
|
@ -117,3 +91,56 @@ class DjangoFilterConnectionField(DjangoConnectionField):
|
|||
self.filterset_class,
|
||||
self.filtering_args,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def connection_resolver(cls,
|
||||
resolver,
|
||||
connection,
|
||||
default_manager,
|
||||
max_limit,
|
||||
enforce_first_or_last,
|
||||
filterset_class,
|
||||
filtering_args,
|
||||
root,
|
||||
info,
|
||||
**args):
|
||||
|
||||
order = args.get('order', None)
|
||||
_parent = args.get('know_parent', False)
|
||||
|
||||
if not _parent:
|
||||
if hasattr(info.parent_type._meta, 'know_parent_fields'):
|
||||
options = info.parent_type._meta.know_parent_fields
|
||||
assert isinstance(options, (list, tuple)), \
|
||||
"know_parent_fields should be list or tuple"
|
||||
_parent = info.field_name in options
|
||||
|
||||
def new_resolver(root, info, **args):
|
||||
filters = dict(filter(lambda x: '__' in x[0], args.items()))
|
||||
qs = resolver(root, info, **args)
|
||||
if qs is None:
|
||||
qs = default_manager.filter()
|
||||
|
||||
if filters:
|
||||
qs = qs.filter(make_qs(filters))
|
||||
|
||||
if order:
|
||||
qs = qs.order_by(order)
|
||||
|
||||
if _parent and root is not None:
|
||||
instances = []
|
||||
for instance in qs:
|
||||
setattr(instance, '_parent', root)
|
||||
instances.append(instance)
|
||||
return instances
|
||||
return qs
|
||||
|
||||
return DjangoConnectionField.connection_resolver(
|
||||
new_resolver,
|
||||
connection,
|
||||
default_manager,
|
||||
max_limit,
|
||||
enforce_first_or_last,
|
||||
root,
|
||||
info,
|
||||
**args)
|
||||
|
|
|
@ -1,23 +1,8 @@
|
|||
import six
|
||||
|
||||
from .filterset import custom_filterset_factory, setup_filterset
|
||||
|
||||
|
||||
def get_filtering_args_from_filterset(filterset_class, type):
|
||||
""" Inspect a FilterSet and produce the arguments to pass to
|
||||
a Graphene Field. These arguments will be available to
|
||||
filter against in the GraphQL
|
||||
"""
|
||||
from ..forms.converter import convert_form_field
|
||||
|
||||
args = {}
|
||||
for name, filter_field in six.iteritems(filterset_class.base_filters):
|
||||
field_type = convert_form_field(filter_field.field).Argument()
|
||||
field_type.description = filter_field.label
|
||||
args[name] = field_type
|
||||
|
||||
return args
|
||||
|
||||
from functools import reduce
|
||||
from neomodel import Q
|
||||
|
||||
def get_filterset_class(filterset_class, **meta):
|
||||
"""Get the class to be used as the FilterSet"""
|
||||
|
@ -26,3 +11,24 @@ def get_filterset_class(filterset_class, **meta):
|
|||
# return it
|
||||
return setup_filterset(filterset_class)
|
||||
return custom_filterset_factory(**meta)
|
||||
|
||||
|
||||
def make_qs(filters):
|
||||
for item in filters.items():
|
||||
if '__equal' in item[0]:
|
||||
filters.pop(item[0])
|
||||
filters[item[0].split("__")[0]] = item[1]
|
||||
return reduce(lambda init, nx: init & Q(**{nx[0]: nx[1]}), filters.items(), Q())
|
||||
|
||||
|
||||
def get_filtering_args_from_filterset(filterset_class, type):
|
||||
from ..forms.converter import convert_form_field
|
||||
args = {}
|
||||
fields = type._meta.model.defined_properties()
|
||||
filterset_fields = filterset_class.Meta.fields or []
|
||||
for name in filterset_fields:
|
||||
field_type = convert_form_field(fields[name]).Argument()
|
||||
field_type.description = "filter"
|
||||
for modificator in type._meta.neomodel_filter_fields[name]:
|
||||
args["{}__{}".format(name, modificator)] = field_type
|
||||
return args
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
from collections import OrderedDict
|
||||
|
||||
from django.utils.functional import SimpleLazyObject
|
||||
from graphene import Field
|
||||
from graphene import Field, Boolean
|
||||
from graphene.relay import Connection, Node
|
||||
from graphene.types.objecttype import ObjectType, ObjectTypeOptions
|
||||
from graphene.types.utils import yank_fields_from_attrs
|
||||
|
@ -15,6 +15,9 @@ from neomodel import (
|
|||
)
|
||||
|
||||
|
||||
KnowParent = dict(know_parent=Boolean(default_value=True))
|
||||
|
||||
|
||||
def construct_fields(model, registry, only_fields, exclude_fields):
|
||||
_model_fields = get_model_fields(model)
|
||||
|
||||
|
@ -55,6 +58,7 @@ class DjangoObjectType(ObjectType):
|
|||
exclude_fields=(),
|
||||
filter_fields=None,
|
||||
neomodel_filter_fields=None,
|
||||
know_parent_fields=None,
|
||||
connection=None,
|
||||
connection_class=None,
|
||||
use_connection=None,
|
||||
|
@ -108,6 +112,7 @@ class DjangoObjectType(ObjectType):
|
|||
_meta.fields = django_fields
|
||||
_meta.connection = connection
|
||||
_meta.neomodel_filter_fields = neomodel_filter_fields
|
||||
_meta.know_parent_fields = know_parent_fields
|
||||
|
||||
super(DjangoObjectType, cls).__init_subclass_with_meta__(
|
||||
_meta=_meta, interfaces=interfaces, **options
|
||||
|
|
Loading…
Reference in New Issue
Block a user