graphene-django
graphene-django copied to clipboard
Custom argument on DjangoObjectType
Hi all 👋🏻,
I've seen #333 but still have a question about how to add a custom argument that'd enable a more complex filter on a DjangoObjectType
.
Using graphene-django==2.8.0
.
class ThingNode(DjangoObjectType):
class Meta:
model = Thing
interfaces = (graphene.relay.Node,)
@classmethod
def get_queryset(cls, queryset, info):
# How do I access my_custom_param here to enable a custom filter?
if my_custom_param:
# Heavily modify the queryset here with filter + exclude
queryset.filter().filter().exclude()
else:
# Something else
queryset.filter().filter()
return queryset
class Query(graphene.ObjectType):
things = DjangoFilterConnectionField(ThingNode, my_custom_param=graphene.Boolean())
Did find a way to support this but it feels like I'm dipping into internals that I shouldn't be?
class ThingNode(DjangoObjectType):
class Meta:
model = Thing
interfaces = (graphene.relay.Node,)
@classmethod
def get_queryset(cls, queryset, info):
field_asts = info.field_asts
if field_asts:
args = field_asts[0].arguments
my_custom_param = [a for a in args if a.name.value == "myCustomParam"]
my_custom_param = my_custom_param[0].value.value if my_custom_param else False
else:
my_custom_param = False
if my_custom_param:
# Heavily modify the queryset here with filter + exclude
queryset.filter().filter().exclude()
else:
# Something else
queryset.filter().filter()
return queryset
class Query(graphene.ObjectType):
things = DjangoFilterConnectionField(ThingNode, my_custom_param=graphene.Boolean())
Sorry to clarify something, I'm aware I can overwrite Query.resolve_things
to handle my_custom_param
, but then I lose the pagination helpers that DjangoFilterConnectionField
provides.
Is there a middle ground there that lets you override the resolver and still let DjangoFilterConnectionField
handle pagination?
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
Is this still relevant? If so, what is blocking it? Is there anything you can do to help move it forward?
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs.
@msukmanowsky, Thank you! Your answer is the solution i was looking for.
Is there a middle ground there that lets you override the resolver and still let DjangoFilterConnectionField handle pagination?
Did you ever come up with a solution along these lines, @msukmanowsky?
Unfortunately not, @ethagnawl.
Thanks for the prompt response, @msukmanowsky!
I'm curious to know if any of the maintainers or anyone else in the community has thoughts on how best to approach this. (I'm also happy to move this conversation to a more appropriate venue!) I'm still coming up to speed on Graphene and GraphQL but, unless I'm missing something, it seems like this would be a very common issue.
Do the examples from the docs on filtering here https://docs.graphene-python.org/projects/django/en/latest/filtering/#custom-filtersets help @msukmanowsky and @ethagnawl? It seems like the original question could be solved with a custom django_filters.FilterSet
class that includes a new field for my_custom_param
, on which you filter your queryset. The docs from django-filter here on how to define your own filtering method
for a FilterSet
may also be handy.
Let me know if I'm misunderstanding, or there's some scenario that's not supported by that.
Thank you for following up and providing these links, @sjdemartini!
I need to read more into the django-filter options. This is my first time using that library and, based on my initial round of research, I was under the impression that this would not be an option in my case because the values I'd need to filter (VectorField) on don't have existing Filter subclasses or backing Django "form fields", which prevents me from easily creating my own. I actually started a "discussion" in the django-filter repository about this yesterday.
To take a step back, though, what I'd ideally be able to do in accordance with what's being discussed above is something like:
def resolve_all_items(parent, info, **kwargs):
if kwargs.get("embedding_distance") and kwargs.get("embedding"):
queryset = Item.alias(
distance=CosineDistance("embedding", kwargs.pop("embedding"))
).filter(distance__lt=kwargs.pop("embedding_distance"))
else:
queryset = Item.objects.none()
# tap into the existing, default behavior which handles filtering of all other fields, pagination, etc.
return default_resolver(parent, info, queryset, kwargs)
@ethagnawl I'm not familiar with pgvector/VectorField
or the nuances of your specific use-case, but you can add filters to a FilterSet
that do not correspond to a Django field or form. In graphene-django v3 there's also a handy TypedFilter
utility to specify the Graphene Type for your FilterSet
filters (see the docs here; they haven't been published properly to the official docs site yet).
For instance, here's a random example of using TypedFilter
:
from graphene_django.filter import TypedFilter
class MyCustomFilterSet(django_filters.FilterSet):
my_custom_param = TypedFilter(graphene.Int, method="my_custom_param_filter")
class Meta:
model = MyModel
fields = "__all__"
def my_custom_param_filter(self, queryset, name, value: int):
if my_custom_param > 5:
return queryset
return queryset.filter(something_else=value)
It seems like you could add a filter field of graphene.String
type (or graphene.List
perhaps?) and do what you need to. The specifics of doing things with VectorField
seem distinct from the original issue posted, so I'm thinking this issue can perhaps be closed.
This is very helpful and, on its face, seems like it should work. Thank you, @sjdemartini!
The specifics of doing things with VectorField seem distinct from the original issue posted, so I'm thinking this issue can perhaps be closed.
Agreed as far as I'm concerned. Apologies for hijacking the thread.
All good, thanks for discussing! I'll close this out, and if there's something outstanding from the original post that seems unaddressed, feel free to follow up @msukmanowsky.