Fix 1061: DjangoListField should not cache queries#1063
Fix 1061: DjangoListField should not cache queries#1063zbyte64 merged 3 commits intographql-python:masterfrom
Conversation
…e/call queryset at view time, first attempt at solution
|
Not sure if this is how we want to fix the issue, seems to me default_queryset should be a callable to be called during view execution time. |
graphene_django/fields.py
Outdated
|
|
||
| def get_default_queryset(self): | ||
| return self.model._default_manager.get_queryset() | ||
| return self.model._default_manager # .get_queryset() |
There was a problem hiding this comment.
The problem comes from line 71
def get_resolver(self, parent_resolver):
_type = self.type
if isinstance(_type, NonNull):
_type = _type.of_type
django_object_type = _type.of_type.of_type
return partial(
self.list_resolver,
django_object_type,
parent_resolver,
self.get_default_queryset(), # <<<< This gets evaluated on init
)Your change basically move the execution to later by making not return a queryset but the queryset manager (that you then force the evaluation via maybe_queryset).
I think it's a bit convoluted. And it's a bit confusing to call it get_default_queryset when we already know it does not return a queryset...
I would change it to something like:
def default_queryset_resolver(self):
return self.model._default_manager.get_queryset
@staticmethod
def list_resolver(
django_object_type, resolver, default_queryset_resolver, root, info, **args
):
queryset = maybe_queryset(resolver(root, info, **args))
if queryset is None:
queryset = default_queryset_resolver()
if isinstance(queryset, QuerySet):
# Pass queryset to the DjangoObjectType get_queryset method
queryset = maybe_queryset(django_object_type.get_queryset(queryset, info))
return queryset
def get_resolver(self, parent_resolver):
return partial(
self.list_resolver,
self._underlying_type, # Do you see any reason why we were not doing that here? if there is a good reason you can change it back
parent_resolver,
self.default_queryset_resolver()
)There was a problem hiding this comment.
I agree the naming is wrong. I don't want to drop get_default_queryset as that is documented in the API, so going forward I think we'll have default_queryset_resolver return get_default_queryset which seems a tad odd. Also wondering if we should incorporate info to be passed to the default_queryset_resolver callable.
There was a problem hiding this comment.
[...] we'll have
default_queryset_resolverreturnget_default_querysetwhich seems a tad odd
I assume you mean the other way around?
If you want to keep the function get_default_queryset I would simply add it as:
def self.get_default_queryset(self):
return default_queryset_resolver()()which is still weird.... :)
But I don't necessarily see the point if it is not used.
You say it's in the documentation but I couldn't find it... all I see is reference to get_queryset on DjangoObjectType which is the method we call here (so if a user had overridden it, it would still work).
There was a problem hiding this comment.
Huh, you're right about the docs. Also in the same file we have DjangoConnectionField which defines resolve_queryset & get_queryset_resolver as well as get_manager which acts as the default queryset. I like the semantics there more, maybe it should adopt those methods, certainly asking for a default manager instead of a queryset is a better contract that prevent caching
There was a problem hiding this comment.
Makes sense 👍
Did you see my comment/question about why we are not using self._underlying_type in the get_resolver() method?
… DjangoConnectionField for a better variable name default_manager instead of default_queryset
Attempt to fix #1061
DjangoListField.get_default_querysetreturns a manager andlist_resolvercalls maybe_queryset on default_queryset