GraphQL + Django:使用原始PostgreSQL查询解析查询

Joh*_*ngo 6 python django postgresql graphql graphene-python

当使用外部数据库从多个表中获取数据时,使用GraphQL和Django的最佳方法是什么(即,创建一个表示数据的Django模型不会对应于我的数据库中的单个表)?

我的方法是暂时放弃使用Django模型,因为我认为我还没有完全理解它们.(我对Django和GraphQL都是新手.)我已经设置了一个带有连接外部Postgres DB的应用程序的简单项目.我遵循了Graphene Django教程中的所有设置,然后在我意识到我创建的模型是几个表的混合物时遇到了障碍.

我有一个查询,它发送回映射到我的模型中的字段的正确列,但我不知道如何使这成为一个动态连接,以便当我的API被命中时,它查询我的数据库并将行映射到模型我在Django中定义的模式.

我的方法是避免使用模型,并使用Steven Luscher的演讲中展示的更简单的方法:在30分钟内实现Zero to GraphQL.

TLDR;

目标是能够访问我的GraphQL端点,使用我的django.db.connection中的游标对象来获取应该解析为OrderItemTypes的GraphQLList的字典列表(见下文).

问题是,当我使用查询命中以下端点时,我为每个值获取空值:

localhost:8000/api?query={orderItems{date,uuid,orderId}}
Run Code Online (Sandbox Code Playgroud)

收益:

{ "data":{ "orderItems":[ {"date":null, "uuid":null, "orderId":null }, ... ] } }
Run Code Online (Sandbox Code Playgroud)

project/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;
    """)
Run Code Online (Sandbox Code Playgroud)

在我的终端中,我从QueryType的resolve方法打印,我可以看到数据从Postgres连接成功返回.但是,GraphQL给了我null,所以它必须在resolve方法中,某些映射被搞砸了.

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

如何将我的数据正确映射到我在OrderItemType中定义的字段?

以下是一些参考资料:

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'
)
Run Code Online (Sandbox Code Playgroud)

文件树

|-- project
    |-- manage.py
    |-- main
        |-- app
            |-- models.py
            |-- schema.py
        |-- schema.py
        |-- settings.py
        |-- urls.py
Run Code Online (Sandbox Code Playgroud)

Syr*_*eto 7

GraphQL上的默认解析器Python/Graphene尝试使用getattr在根对象中解析给定的field_name.因此,例如,名为的字段的默认解析器order_items将类似于:

def resolver(root, args, context, info):
    return getattr(root, 'order_items', None)
Run Code Online (Sandbox Code Playgroud)

知道在进行a getattrdict,结果将是None(用于访问dict项目,你将不得不使用__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;
    """)
Run Code Online (Sandbox Code Playgroud)

希望这可以帮助!