New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Documentation for Relay Edge implementation #59
Comments
@thebritican I'm still thinking a good way for handling the case you commented. |
While we investigate a solution to this, is there a way to incorporate an If not, my next intermediate solution was to produce the schema with Graphene and then introspect the produced schema, modifying the payload. As for how Graphene could implement this: we only need a |
@thebritican meanwhile you can use raw So this would work: class AddTodo(relay.Mutation):
class Input:
text = graphene.String(required=True)
todoEdge = graphene.Field(theGraphQLCoreType) I will try to have this working with graphene around this week. PS: Graphene is using |
I see the I'll take a look at this next week if it's still open and try a PR. Thanks for the great work so far! |
@thebritican Did you find a work-around for this? I'm creating a cursor via @syrusakbary What would the "theGraphQLCoreType" be? My connection is to objects of type |
Current implementation of |
@syrusakbary I'm currently struggling with this too. I have something like this: class CreateEvent(graphene.Mutation):
class Input:
description = graphene.String()
start_date = graphene.String()
end_date = graphene.String()
# ...
all_day = graphene.Boolean()
event_edge = graphene.Field('EventNodeEdge')
event = graphene.Field(EventNode)
@classmethod
def mutate(cls, instance, args, info):
event = Event.objects.create(**args)
cursor = cursor_for_object_in_connection([e.id for e in Event.objects.all()], event.id)
assert cursor
return cls(
event=event,
# Side question: Which Edge class should this be? I see two:
# graphql_relay.connection.connectiontypes.Edge
# graphene.relay.Edge
event_edge=Edge(
node=event,
cursor=cursor,
)
) Currently I'm receiving "Cannot return null for non-nullable field EventNodeEdge.cursor.". However, Any help here would be very welcome indeed! |
This is driving me up the wall. Still receiving |
@adamcharnock I will work on this ASAP (is superbowl here so probably in the weekend will be difficult). |
Thank you very much @syrusakbary, that'll be really useful :-) For now I've managed a temporary workaround by just doing |
BTW, a couple tips for anyone else looking at this. You can grab a reference to a Relay edge like so. from graphene.relay.types import Edge
MyEdge = Edge.for_node(MyNode) Also, if you're using array cursors for now, this is probably the most performant option. from graphql_relay.connection.arrayconnection import cursor_for_object_in_connection
cursor_for_object_in_connection(list(MyModel.object.values_list('id', flat=True)), my_model.id) |
@adamcharnock I'm revising this now. Let me know if that's the problem. |
@syrusakbary I could swear I replied to this! Sorry for the delay. I had actually using both |
Let me know! I would like to find a good solution for this!! :) |
@syrusakbary Will do. To clarify, what I mean is I think that a working example in the docs would really help a lot. I felt I had run out of avenues to explore when I last looked at this. |
Any new progress about this issue? |
I've found a tricky way to get the new edge, which may help. class AddTodo(relay.ClientIDMutation):
class Input:
text = String(required=True)
viewer = Field(User)
todoEdge = Field(relay.types.Edge.for_node(Todo))
@classmethod
def mutate_and_get_payload(cls, input, info):
viewer = get_viewer(info.request_context)
todo = viewer.todos.create(text=input.get("text"))
# get cursor here!
cursor = offset_to_cursor(viewer.todos.count() - 1)
edge = relay.types.Edge.for_node(Todo)(node=todo, cursor=cursor)
return AddTodo(viewer=viewer, todoEdge=edge) |
@mickeyinfoshan that assumes your edge is at the end of the list. If it's somewhere in the list, but you don't know the offset, this won't work. |
Fundamentally, index-based cursors are never going to work, because you need the full context of the list to know what the index will be. In a relay context, that means a different cursor for every sort/filter combo that might be addressing the edge. The data required for that is out of control, IMO. Stepping back, the only requirement for a cursor is that you can select before and after it. Thus, the simplest type of non-index cursor is the sort value for the list. For example, say you have a list of objects with Optionally, if you want the sort field to be dynamic, you can use an object's unique As far as I can tell, once of these two options is required to solve this issue. |
@defrex It is just a tricky way to get the new cursor while performing a RANGE_ADD request, where the new edge should be the last one. It's not a clean one but a fast one for the reason that you don't need to iterate the list at all. In addition, RANGE_ADD is the only situation that a cursor is required on the server-side explicitly, as far as I'm concerned. |
Is there any temporary version of documentation on this? Perhaps in another branch? |
@varunarora @defrex @thebritican in the version 1.0 of Graphene Relay edges and connections are created in a easier, cleaner and more understandable way. Please take a look! |
Broken link (https://github.com/graphql-python/graphene/blob/1.0/graphene/relay/tests/test_connection.py) Also, the official example doesn't seem to implement creating edges in the mutation. So I'm still getting: Error: Cannot query field "createItemEdge" on type "CreateItemMutationPayload". Because My parent class defines: |
Here's how I create an edge. There might be a better solutions, but hope this will help for now.
Following link explains how to create custom connection. |
Thanks @varuna82 really helpful. |
I'm using graphene-django 1.3 and neither |
@reinierpd Closing the issue as I think Connections are better defined in later versions of Graphene. |
Since I struggled a lot to expose a newly created item as edge, here is a complete example using Graphene 2.0, building on the suggestions from @mickeyinfoshan and @varuna82. Hope this is of some help: import graphene
from graphene import relay
from graphene_django import DjangoObjectType
from graphql_relay.connection.arrayconnection import offset_to_cursor
from myapp import models
class Topic(DjangoObjectType):
class Meta:
model = models.Topic
interfaces = (relay.Node, )
TopicEdge = Topic._meta.connection.Edge
class CreateTopic(relay.ClientIDMutation):
class Input:
title = graphene.String(required=True)
topic = graphene.Field(Topic)
topic_edge = graphene.Field(TopicEdge)
@classmethod
def mutate_and_get_payload(cls, root, info, **input):
topic = models.Topic.objects.create(title=input['title'], created_by=user)
edge = TopicEdge(cursor=offset_to_cursor(0), node=topic)
return CreateTopic(topic=topic, topic_edge=edge) |
…hon#59-fix-test-django-settings graphql-python#59 fix test django settings
In the Relay mutation docs, performing RANGE_ADD requires that the new edge created by the mutation is provided in the payload.
I do not know how to provide the correct cursor/
Edge
construction within therelay.ClientIDMutation
subclass.The following code I wrote:
new_object_edge = graphene.Field('ObjectEdge')
I believed would solve my issue, with a proper
resolve_new_object_edge(...)
resolver.However, my resolver cannot reference the
relay.ConnectionField
that is specified on one of the other object types in my mutation, since it only has the context of my (in this case, Django) objects.I experimented with
relay.Edge.for_node(SomeObjectType)
as well but all of the solutions I have tried so far modify the schema correctly but cannot return the appropriateEdge
.The implementation of this scenario in javascript can be found here in the Relay examples directory.
Any idea of a best approach? I can take this question to stack overflow, but felt that people using Relay & Graphene would enjoy seeing an example solution in the docs.
The text was updated successfully, but these errors were encountered: