Graphene-django: Node fields and ids with clean URLs

Created on 13 Apr 2017  路  6Comments  路  Source: graphql-python/graphene-django

I'm using Graphene with Django and Relay (and react-router-relay), but struggling to figure out a good way of:

  • Having an entry point of say /thread/:id/ where id is the plain ID of a Django model instance (ie. a simple number like 7, so /thread/7/)
  • Using this with GraphQL fragments to query a node where id needs to be the opaque global ID, eg:
  fragment on Viewer {
     thread(id: "VGhyZWFkVHlwZToxMjU2OQ==") {
        ...
     }
   }

With the way Graphene-Django works when you modify your schemas for Relay, there doesn't seem to be any way to query a node without that global ID.

Is this intentional? If so, how are you supposed to get from one form of ID to the other in your logic? The project I'm working on currently works with fine with the global ID in the URL, but that's a really ugly and long URL in comparison to just the simple ID.

wontfix

Most helpful comment

@matthewjohnston4 so far here is my understanding of using Node and filters w/o relay:

from graphene import Node
from graphene_django.types import DjangoObjectType

# Custom node to get rid of global id
class CustomNode(Node):
    class Meta:
        name = 'Node'

    @staticmethod
    def to_global_id(type, id):
        return id

    @staticmethod
    def get_node_from_global_id(id, context, info, only_type=None):
        return info.return_type.graphene_type._meta.model.objects.get(id=id)

# My example model
class MyModelType(DjangoObjectType):
    class Meta:
        model = MyModel
        interfaces = (CustomNode, )
        filter_fields = {
            'title': ['exact', 'icontains', 'istartswith'],
        }

# Query Schema
class Query(ObjectType):
    my_model_by_id = CustomNode.Field(MyModelType)
    all_my_model = DjangoFilterConnectionField(MyModelType)

@syrusakbary any thoughts of how to decouple filtering/node from relay?

All 6 comments

Alternatively, reading some other issues, it seems like I shouldn't be using relay.Node.Field in my schemas for my node types (instead just graphene.Field). In this case, all of my node queries _only_ accept the plain ID, and not the global ID.

If I approach it from that direction, how am I supposed to query for a node using that global ID? I've got node = graphene.relay.Node.Field(id=graphene.ID(required=True)) in my root Query in my root schema, yet all I can ever query from this node is id and __typename.

@matthewjohnston4 so far here is my understanding of using Node and filters w/o relay:

from graphene import Node
from graphene_django.types import DjangoObjectType

# Custom node to get rid of global id
class CustomNode(Node):
    class Meta:
        name = 'Node'

    @staticmethod
    def to_global_id(type, id):
        return id

    @staticmethod
    def get_node_from_global_id(id, context, info, only_type=None):
        return info.return_type.graphene_type._meta.model.objects.get(id=id)

# My example model
class MyModelType(DjangoObjectType):
    class Meta:
        model = MyModel
        interfaces = (CustomNode, )
        filter_fields = {
            'title': ['exact', 'icontains', 'istartswith'],
        }

# Query Schema
class Query(ObjectType):
    my_model_by_id = CustomNode.Field(MyModelType)
    all_my_model = DjangoFilterConnectionField(MyModelType)

@syrusakbary any thoughts of how to decouple filtering/node from relay?

Hey @matthewjohnston4, the "node" resolver is a relay-specific endpoint. It is used by the relay client to prevent over-fetching. It is not intended to ever be used for anything else. In this case, you would want to create a field similar to what @khankuan showed, but there is also no need to create a CustomNode... so you could simply do:

class Thread(DjangoObjectType):

  @staticmethod
  def get_thread_for_id(id)
    return self._meta.model.objects.get(id=id)

  class Meta:
    model = Thread
    interfaces = (relay.Node, )


class Query(ObjectType):
  node = relay.Node.Field() # This is for relay's internal use only.
  thread = graphene.Field(Thread, id=graphene.String())
  all_threads = DjangoFilterConnectionField(Thread)

  def resolve_thread(self, args, context, info):
    return Thread.get_thread_for_id(id=args.get('id))

@nickhudkins I had to override CustomNode to retain the regular id otherwise relay id will be used.

No matter how the global ID is necessary to relay, id is very important to Django models, because it is often used as a handler to a object. If the id field is overridden, most normal logic will break.
Hope that there could be a way to rename the global ID, or some instructions on how to rename the model id.

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.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

khankuan picture khankuan  路  4Comments

Northshoot picture Northshoot  路  4Comments

MythicManiac picture MythicManiac  路  3Comments

BrianChapman picture BrianChapman  路  3Comments

dan-klasson picture dan-klasson  路  4Comments