Code
class ShipNode(DjangoObjectType):
class Meta:
model = Ship
filter_fields = ['id', 'size']
interfaces = (relay.Node, )
rowid = graphene.String()
@resolve_only_args
def resolve_rowid(self):
return self.id
class Query(object):
ship = relay.Node.Field(ShipNode)
all_ships = DjangoFilterConnectionField(ShipNode)
I have an id field in my database which gets override when querying GraphQL server by the custom global ID. Using resolve_rowid, I can get the id I want, but I am unable to use it in the filters.
query {
ship(id: "124567") {
}
}
How can I filter based on my database's primary key instead of the global ID? How can I rename global ID from id to _id?
I'm in the same boat. Pretty certain it's a misunderstanding on our end of how relay works with its global id's, but still can't get my head around how to get an object with its model pk 馃槥
Since ShipNode implements the relay.Node interface you should get an id field on ShipNode which will be the global ID for that node (a global ID by default is just the base64("{type}:{id}")
So in your example you should be able to do:
query {
allShips {
edges {
node {
id
}
}
}
}
which should give you something like:
'allShips': {
'edges': [
{
'node': {
'id': 'U2hpcE5vZGU6MTI0NTY3',
}
},
// more nodes
]
}
Then using that ID you should be able to query the ship field:
query {
ship(id: "U2hpcE5vZGU6MTI0NTY3") {
... on ShipNode {
size
}
}
}
Notice that if you base64 decode U2hpcE5vZGU6MTI0NTY3 you get ShipNode:124567
@samkit-jain
How can I rename global ID from id to _id?
At this time, even the monkey-patching at runtime of classes Node, AbstractNode and NodeField from file _graphene/relay/node.py_ does not help (spent 2 days to figure it out).
You need to manually patch the graphene lib in this file and rename shadowing id to something you like (maybe, "global_id"?):
class NodeField(Field):
def __init__(self, node, type=False, deprecation_reason=None, name=None, **kwargs):
assert issubclass(node, Node), "NodeField can only operate in Nodes"
self.node_type = node
self.field_type = type
super(NodeField, self).__init__(
# If we don's specify a type, the field type will be the node
# interface
type or node,
description="The ID of the object",
global_id=ID(required=True), # <<<<<<<<<<< there
)
def get_resolver(self, parent_resolver):
return partial(self.node_type.node_resolver, get_type(self.field_type))
class AbstractNode(Interface):
class Meta:
abstract = True
@classmethod
def __init_subclass_with_meta__(cls, **options):
_meta = InterfaceOptions(cls)
_meta.fields = OrderedDict(
global_id=GlobalID(cls, description="The ID of the object.") # <<<<<<<< there
)
super(AbstractNode, cls).__init_subclass_with_meta__(_meta=_meta, **options)
class Node(AbstractNode):
"""An object with an ID"""
@classmethod
def Field(cls, *args, **kwargs): # noqa: N802
return NodeField(cls, *args, **kwargs)
@classmethod
def node_resolver(cls, only_type, root, info, global_id): # <<<<<<<<<<<<<< there
return cls.get_node_from_global_id(info, global_id, only_type=only_type) # <<<<<<<<<<<<<< and there
then do this
class ShipNode(DjangoObjectType):
def resolve_global_id(self, info):
return self.pk
but even then the filtering by good old digital id will require additional work.
How can I filter based on my database's primary key instead of the global ID?
You need to give up using relay.Node and relay.Node.Field (and all their magic) and do something like
class ShipNode(DjangoObjectType):
class Meta:
model = Ship
# filter_fields = ['id', 'size']
# interfaces = (relay.Node, )
class Query(object):
ship = graphene.Field(ShipNode, id=graphene.String(required=True))
def resolve_ship(self, info, id):
return Ship.objects.get(pk=id)
I think the people behind the graphene-django missed out how confusing, frustrating and all other dirty epithets is to work with shadowed-out id of an object.
@webel
how to get an object with its model pk
class ShipNode(DjangoObjectType):
pk = graphene.Int(source='pk')
What's the workaround here? While I like the Global ID declaration of Graphene-Relay, I hate that it shadows the ID field and makes it impossible to filter & query Connection objects using ID in Relay.
One option is, of course, to redeclare the ID in a pk field, but this is tedious and verbose. Also, to get filtering to work, the FilterSet also needs an explicit pk declaration :(
Any ideas on how to enable:
Also posted a question on StackOverflow here:
https://stackoverflow.com/questions/54328681/enable-pk-based-filtering-in-django-graphene-relay-while-retaining-global-ids/54344308#54344308
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.
Can we keep this issue open please? I'm vexed.
Can someone explain in an example, hopefully in a repo, with a CI test, how we override ID to return the database pk?
I have tried various stabs at monkeypatching and still can't get the actual ID to display. Lookups work. But I am using UUID Primary Keys and they're not showing in the results.
My temp solution is to rename all models' id's to uid = models.AutoField(primary_key=True)
I have added 2 PRs in the graphene repository that could solve this issue I think: https://github.com/graphql-python/graphene/issues/1276
Most helpful comment
I think the people behind the graphene-django missed out how confusing, frustrating and all other dirty epithets is to work with shadowed-out id of an object.