GraphQL + Django: разрешение запросов с помощью необработанного запроса PostgreSQL
как лучше всего использовать GraphQL с Django при использовании внешней базы данных для извлечения данных из нескольких таблиц (т. е. создание модели Django для представления данных не будет соответствовать одной таблице в моей базе данных)?
мой подход состоял в том, чтобы временно отказаться от использования моделей Django, так как я не думаю, что полностью их понимаю. (Я совершенно новичок в Django, а также в GraphQL.) Я создал простой проект с приложением с подключенным внешним Postgres DB. Я следил за всеми настройками из графен Django учебник и затем врезался в дорожный блок, когда я понял, что модель, которую я создал смесь из нескольких таблиц.
у меня есть запрос, который отправляет обратно правильные столбцы, сопоставленные полям в моей модели, но я не знаю, как сделать это динамическое соединение таким образом, что когда мой API попадает, он запрашивает мою базу данных и сопоставляет строки схеме модели, которую я определил в Django.
мой подход с чтобы избежать моделей и использовать более простой метод, продемонстрированный в выступлении Стивена Люшера: ноль до GraphQL за 30 минут.
TLDR;
цель состоит в том, чтобы иметь возможность попасть в мою конечную точку GraphQL, использовать объект курсора из моего django.децибел.подключение для получения списка словарей, которые должны разрешаться в GraphQLList OrderItemTypes (см. ниже).
проблема в том, что я получаю нули для каждого значения, когда я нажимаю следующая конечная точка с запросом:
localhost:8000/api?query={orderItems{date,uuid,orderId}}
возвращает:
{ "data":{ "orderItems":[ {"date":null, "uuid":null, "orderId":null }, ... ] } }
проект / main/app/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;
""")
в моем терминале я печатаю из метода разрешения QueryType, и я вижу, что данные успешно возвращаются из моего соединения Postgres. Однако GraphQL дает мне nulls, поэтому он должен быть в методе resolve, что какое-то сопоставление становится испорченным.
[ { 'uuid': u'7584aac3-ab39-4a56-9c78-e3bb1e02dfc1', 'order_id': 25624320, 'date': datetime.datetime(2016, 1, 30, 16, 39, 40, 573400, tzinfo=<UTC>) }, ... ]
Как правильно сопоставить мои данные в поля я определил в моей OrderItemType?
вот еще несколько ссылок:
проект / main/schema.py
import graphene
from project.app.schema import QueryType AppQuery
class Query(AppQuery):
pass
schema = graphene.Schema(
query=Query, name='Pathfinder Schema'
)
дерево
|-- project
|-- manage.py
|-- main
|-- app
|-- models.py
|-- schema.py
|-- schema.py
|-- settings.py
|-- urls.py
2 ответов
решатели по умолчанию на GraphQL Python / Graphene пытаются выполнить разрешение данного имени Поля в корневом объекте с помощью getattr.
Например, распознаватель по умолчанию для поля с именем order_items
будет что-то вроде:
def resolver(root, args, context, info):
return getattr(root, 'order_items', None)
зная, что, делая getattr
на dict
, то результатом будет None
(для доступа к элементам словаря, вам придется использовать __getitem__
/ dict[key]
).
таким образом, решение вашей проблемы может быть так же просто, как изменение от dicts
для хранения содержание 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;
""")
надеюсь, что это помогает!
вот временное решение, хотя я надеюсь, что есть что-то более чистое для обработки snake_cased fieldnames.
проект / main/app/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's _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()