GraphQL + Django: allow queries using a raw PostgreSQL query

What is the best way to use GraphQL with Django when using an external database to retrieve data from multiple tables (i.e. creating a Django model to represent data will not match a single table in my database)?

My approach was to temporarily abandon the use of Django models, as I don't think I fully understand them. (I am completely new to Django, and also to GraphQL.) I created a simple project with an application with Postgres external mail connected. I made all the settings from the Graphene Django tutorial , and then hit the road block when I realized that the model I created was a combination of several tables.

I have a query that sends the correct columns mapped to the fields in my model, but I don’t know how to make this dynamic connection, so when my API gets there, it queries my database and displays the rows to the model schema that I defined in Django

My approach since then has been to avoid models and use the simpler method demonstrated in Steven Lusher's talk: β€œZero” in GraphQL in 30 minutes .

TL; DR;

The goal is to be able to hit my GraphQL endpoint, use the cursor object from my django.db.connection to get a list of dictionaries that should be resolved to GraphQLList from OrderItemTypes (see below).

The problem is that I get zeros for each value when I click the following endpoint with the request:

localhost:8000/api?query={orderItems{date,uuid,orderId}} 

returns:

 { "data":{ "orderItems":[ {"date":null, "uuid":null, "orderId":null }, ... ] } } 

Project / home / application /schema.py

 import graphene from django.db import connection class OrderItemType(graphene.ObjectType): date = graphene.core.types.custom_scalars.DateTime() order_id = graphene.ID() uuid = graphene.String() class QueryType(graphene.ObjectType): name = 'Query' order_items = graphene.List(OrderItemType) def resolve_order_items(root, args, info): data = get_order_items() # data prints out properly in my terminal print data # data does not resolve properly return data def get_db_dicts(sql, args=None): cursor = connection.cursor() cursor.execute(sql, args) columns = [col[0] for col in cursor.description] data = [ dict(zip(columns, row)) for row in cursor.fetchall() ] cursor.close() return data def get_order_items(): return get_db_dicts(""" SELECT j.created_dt AS date, j.order_id, j.uuid FROM job AS j LIMIT 3; """) 

In my terminal, I print from the QueryType resolution method, and I see that the data is successfully returned from my Postgres connection. However, GraphQL gives me nulls, so it should be in the resolution method that some kind of mapping gets messed up.

 [ { 'uuid': u'7584aac3-ab39-4a56-9c78-e3bb1e02dfc1', 'order_id': 25624320, 'date': datetime.datetime(2016, 1, 30, 16, 39, 40, 573400, tzinfo=<UTC>) }, ... ] 

How to correctly match my data with the fields that I defined in my OrderItemType?

Here are some more links:

Project / main / schema.py

 import graphene from project.app.schema import QueryType AppQuery class Query(AppQuery): pass schema = graphene.Schema( query=Query, name='Pathfinder Schema' ) 

file tree

 |-- project |-- manage.py |-- main |-- app |-- models.py |-- schema.py |-- schema.py |-- settings.py |-- urls.py 
+5
source share
2 answers

The default Python / Graphene GraphQL relators try to resolve the given field name in the root object using getattr. So, for example, the default recognizer for a field named order_items will look something like this:

 def resolver(root, args, context, info): return getattr(root, 'order_items', None) 

Knowing that when executing getattr in a dict result will be None (to access the dict elements you will need to use __getitem__ / dict[key] ).

So, solving your problem can be as simple as changing from dicts to storing content to namedtuples .

 import graphene from django.db import connection from collections import namedtuple class OrderItemType(graphene.ObjectType): date = graphene.core.types.custom_scalars.DateTime() order_id = graphene.ID() uuid = graphene.String() class QueryType(graphene.ObjectType): class Meta: type_name = 'Query' # This will be name in graphene 1.0 order_items = graphene.List(OrderItemType) def resolve_order_items(root, args, info): return get_order_items() def get_db_rows(sql, args=None): cursor = connection.cursor() cursor.execute(sql, args) columns = [col[0] for col in cursor.description] RowType = namedtuple('Row', columns) data = [ RowType(*row) # Edited by John suggestion fix for row in cursor.fetchall() ] cursor.close() return data def get_order_items(): return get_db_rows(""" SELECT j.created_dt AS date, j.order_id, j.uuid FROM job AS j LIMIT 3; """) 

Hope this helps!

+6
source

Here is a temporary workaround, although I hope there is something cleaner to handle snake_cased names.

Project / home / application /schema.py

 from graphene import ( ObjectType, ID, String, Int, Float, List ) from graphene.core.types.custom_scalars import DateTime from django.db import connection ''' Generic resolver to get the field_name from self _root ''' def rslv(self, args, info): return self.get(info.field_name) class OrderItemType(ObjectType): date = DateTime(resolver=rslv) order_id = ID() uuid = String(resolver=rslv) place_id = ID() ''' Special resolvers for camel_cased field_names ''' def resolve_order_id(self, args, info): return self.get('order_id') def resolve_place_id(self, args, info): return self.get('place_id') class QueryType(ObjectType): name = 'Query' order_items = List(OrderItemType) def resolve_order_items(root, args, info): return get_order_items() 
0
source

All Articles