DjangoFilterConnectionField and resolve support
See original GitHub issueI found that the following example from the docs doesn’t work correctly.
The thing is that if there is a resolver for a DjangoFilterConnectionField like in the example, the filtering and ordering created by DjangoFilterConnectionField will be ignored. Details below.
class Query(ObjectType):
my_posts = DjangoFilterConnectionField(CategoryNode)
def resolve_my_posts(self, args, context, info):
# context will reference to the Django request
if not context.user.is_authenticated():
return Post.objects.none()
else:
return Post.objects.filter(owner=context.user)
The issues seems to be that DjangoFilterConnectionField ignores what the resolver returns.
@staticmethod
def connection_resolver(resolver, connection, default_manager, filterset_class, filtering_args,
root, args, context, info):
filter_kwargs = {k: v for k, v in args.items() if k in filtering_args}
order = args.get('order_by', None)
qs = default_manager.get_queryset()
if order:
qs = qs.order_by(order)
qs = filterset_class(data=filter_kwargs, queryset=qs)
return DjangoConnectionField.connection_resolver(resolver, connection, qs, root, args, context, info)
Here the QuerySet is built just fine but the resolver does not have an impact on the QuerySet (which is fine if no resolver is used anyway).
The problem arrises when the DjangoConnectionField resolves the query.
@staticmethod
def connection_resolver(resolver, connection, default_manager, root, args, context, info):
iterable = resolver(root, args, context, info)
if iterable is None:
iterable = default_manager
iterable = maybe_queryset(iterable)
if isinstance(iterable, QuerySet):
_len = iterable.count()
else:
_len = len(iterable)
connection = connection_from_list_slice(
iterable,
args,
slice_start=0,
list_length=_len,
list_slice_length=_len,
connection_type=connection,
edge_type=connection.Edge,
pageinfo_type=PageInfo,
)
connection.iterable = iterable
connection.length = _len
return connection
Here we consider the default_manager (which is the QuerySet that was built by DjangoFilterConnectionField) only if there resolver is None.
So you either can have the DjangoFilterConnection field or a resolver. But it should be possible to use both (to limit an initial QuerySet and check of authorisation, etc.).
Solution
The solution I use now locally by changing DjangoFilterConnectionField is the following:
@staticmethod
def connection_resolver(resolver, connection, default_manager, filterset_class, filtering_args,
root, args, context, info):
filter_kwargs = {k: v for k, v in args.items() if k in filtering_args}
order = args.get('order_by', None)
def new_resolver(root, args, context, info):
qs = resolver(root, args, context, info)
if qs is None:
qs = default_manager.get_queryset()
if order:
qs = qs.order_by(order)
qs = filterset_class(data=filter_kwargs, queryset=qs)
return qs
return DjangoConnectionField.connection_resolver(new_resolver, connection, None, root, args, context, info)
Basically it wraps the resolver and uses its QuerySet if available or create one with the default_manager. It also guarantees that the resolver returns something and therefore we can set the default_manager to None just to make sure it there is no other way to get to the QuerySet.
I would have created a PR but I couldn’t get the tests of the repo running locally. 😦
PS: I’d also really like to get the tests running so I can contribute to the code directly. Any pointers how to get it running are really appreciated as I didn’t have luck by following the instructions in “Contributing”.
Issue Analytics
- State:
- Created 7 years ago
- Reactions:7
- Comments:18 (6 by maintainers)
Top GitHub Comments
The above solution also fixes #21
@ayozemr I haven’t got the tests running (haven’t tried it since November though.).
My recommended solution would be to just use a custom class for this and override the function like I did above.
I now use this class instead of the DjangoFilterConnectionField. Not the prettiest solution but until the issue is fixed this works.
If someone gets the tests running, I’m happy to create a PR with the fix.