How to extend connection info? #1185
-
Hi, {
"data": {
"my_function": {
"custom_object": {
"field1": 101,
"field2": 66,
},
"pageInfo": {
"endCursor": "foo"
},
"edges": [
{
"node": {
"id": "bar",
}
},
...
]
}
}
} Python 2.7 Thank you! |
Beta Was this translation helpful? Give feedback.
Replies: 16 comments 1 reply
-
One way to do it for fields at the same level as an edge is to attach an attribute to the node, when you resolve them. And then to resolve for the field at the same level as edges by assessing the attribute. you can do something similar at the connection level .. I explained this, tho, in more detail to you at graphql-python/graphene#573
|
Beta Was this translation helpful? Give feedback.
-
I haven't actually tried this, but looking at the code for the graphene relay connection, and following it's So I'm thinking this would work?
And if it's something you want on all your connections, I think you could have the custom_object stuff on a base class and make all your connections use that. I'd love to hear what ends up working for you. |
Beta Was this translation helpful? Give feedback.
-
Hi @spockNinja, @japrogramer, Thank you for the advice! Example: tags = relay.ConnectionField(TagConnection)
def resolve_tags(self, info, **kwargs):
some_count_var = Tag.objects.count()
return Tag.objects.get(pk=1) then class TagConnection(relay.Connection):
class Meta:
node = TagNode
total_count = graphene.Int()
resolve_total_count(self, info):
# here I somehow access the `some_count_var` variable and return
return some_count_var hope this all makes sense! P.S. |
Beta Was this translation helpful? Give feedback.
-
I can definitely see what you're trying to accomplish. Without whipping up a full example to test, I'm honestly not too sure of the best place to pass that information up, or if it should be passed up at all. I'm thinking that if something is going to live outside of the If you want some custom information showing up that is dependent on resolutions inside of |
Beta Was this translation helpful? Give feedback.
-
Not sure if that solves it, but I just did this to get totalCount back into the a DjangoConnection (without coupling it to an ObjectType): class CountingConnectionField(graphene_django.DjangoConnectionField):
@property
def type(self):
node_type = super(graphene.relay.ConnectionField, self).type
assert issubclass(node_type, graphene_django.DjangoObjectType), "DjangoConnectionField only accepts DjangoObjectType types"
class Connection(graphene.Connection):
total_count = graphene.Int()
class Meta:
name = node_type._meta.name + 'Connection'
node = node_type
def resolve_total_count(self, info, **kwargs):
return self.iterable.count()
return Connection The only tricky part in this is to call the super() such that |
Beta Was this translation helpful? Give feedback.
-
@nuschk thank you for this example. I had a similar case where I wanted to add a "conveniences" Does this extension have other implications you are aware of? Possible side-effects? Also, it would be great to have this supported within graphene-django out of the box by just declaring a field and a resolve function on a subclass of DjangoConnectionField. |
Beta Was this translation helpful? Give feedback.
-
@nuschk well I ran into a complication with the mentioned approach. I tried to use it for multiple connections and it errors out with |
Beta Was this translation helpful? Give feedback.
-
Well, my brain always melts when I dive into graphene's meta mechanics, so I'm not entirely sure my advice will be sound. However, the problem is, that the inner class ListingConnection(graphene.Connection):
total_count = graphene.Int()
class Meta:
node = types.Listing
def resolve_total_count(self, info, **kwargs):
return self.iterable.count()
class CountingListingConnectionField(graphene_django.DjangoConnectionField):
@property
def type(self):
return ListingConnection Again, please take this with a grain of salt. I don't completely understand what's going on. :-) |
Beta Was this translation helpful? Give feedback.
-
@nuschk wow. Thats a horrible amount of boilerplate code to simply add total field, especially considering that it should be done for each type... |
Beta Was this translation helpful? Give feedback.
-
@aldarund Heh, well, I agree, although I think at least it's readable. There might be something clever I'm missing. |
Beta Was this translation helpful? Give feedback.
-
I made the code reusable. class CountingNodeConnectionField(DjangoFilterConnectionField):
def __init__(self, type, fields=None, order_by=None,
extra_filter_meta=None, filterset_class=None,
*args, **kwargs):
self._type = type
self._fields = fields
self._provided_filterset_class = filterset_class
self._filterset_class = None
self._extra_filter_meta = extra_filter_meta
self._base_args = None
super(DjangoFilterConnectionField, self).__init__(
type,
*args,
**kwargs
)
@property
def type(self):
class NodeConnection(graphene.Connection):
total_count = graphene.Int()
class Meta:
node = self._type
name = '{}Connection'.format(self._type._meta.name)
def resolve_total_count(self, info, **kwargs):
return self.iterable.count()
return NodeConnection IMHO But it is not a beautiful. |
Beta Was this translation helpful? Give feedback.
-
@TheRexx thanks! It works for me with a small change to avoid class names clashing:
But it works only for those connections declared in the query root, not the auto-generated for each type related list. Any idea of how to cover that case too? |
Beta Was this translation helpful? Give feedback.
-
@greenled what do you mean auto generated connections? |
Beta Was this translation helpful? Give feedback.
-
@greenled You can bring this code to a separate module, and connect to the schema where it is needed. In any case, you prescribe nodes for each model manually and they have different parameters. If I did not understand your question correctly, please add a sample code. |
Beta Was this translation helpful? Give feedback.
-
For future reference, see if this solution fits your needs: |
Beta Was this translation helpful? Give feedback.
-
Solution so that class ExtendedConnection(graphene.relay.Connection):
class Meta:
abstract = True
@classmethod
def __init_subclass_with_meta__(cls, node=None, name=None, **options):
result = super().__init_subclass_with_meta__(node=node, name=name, **options)
cls._meta.fields["total_count"] = graphene.Field(
type=graphene.Int,
name="totalCount",
description="Number of items in the queryset.",
required=True,
resolver=cls.resolve_total_count,
)
return result
def resolve_total_count(self, *_) -> int:
return self.iterable.count()
class Bla(DjangoObjectType):
class Meta:
connection_class = ExtendedConnection
|
Beta Was this translation helpful? Give feedback.
For future reference, see if this solution fits your needs:
#636 (comment)